Skip to main content

USAGE TELEMETRY EVENTS

The Swamp CLI records one usage telemetry event per command invocation. This page describes the transport, every field of the event, what the ingest service adds, and the redaction policy. For opt-outs, the spool and delivery, see the Usage Telemetry reference. For the reasoning behind these choices, see About usage telemetry.

The envelope and redaction on this page were verified against Swamp 20260930.201417.0-sha.69135e9d.

Transport

Events

Source Events
Any CLI command One cli_invocation event.
swamp workflow run One event for the command, plus one child event per model-method step. Children carry parentInvocationId.
swamp serve One event per workflow run and per model-method run it executes, plus one event for the server process when it exits.

The model-method steps inside a nested workflow emit child events, and at any depth those children report the top-level run's runId and workflowName. A step still running when the run ends is recorded as an error.

Events are recorded for commands the CLI parses.

Request

Property Value
Method POST
URL <endpoint>/ingest
Content type application/json
Single event The event object, bare
Batch { "events": [ ... ] }
Batch limits 500 events and 1 MiB per request
Delivered HTTP 202. Other statuses keep the entry in the spool for retry.

Headers:

Header Sent Value
User-Agent Always swamp-cli/<swampVersion>
x-api-key When an API key is stored by swamp auth login The stored API key

The request body identifies the install and the repository by random UUIDs. See What the ingest service adds for what the server attaches when x-api-key is present.

Flushing

Process When Entries Time limit
CLI command At the end of every command Up to 25 unsent entries, oldest day first 2 seconds
swamp serve Every 60 seconds Up to 20 batches of unsent entries 10 seconds

When a flush fails, the CLI prints a warning and keeps the entries in the spool for the next invocation:

[WRN] swamp·cli: Telemetry flush failed (network error) — entries are queued locally and will retry on the next invocation

When swamp serve fails to deliver a batch repeatedly, it retries the entries one at a time and quarantines entries that keep failing.

Endpoint

The endpoint is resolved in this order. The first match wins. Each event is stamped with the endpoint in effect when it was recorded; see Delivery.

Priority Source
1 SWAMP_TELEMETRY_ENDPOINT environment variable
2 telemetryEndpoint in .swamp.yaml
3 http://localhost:8080 when the auth server is localhost, 127.0.0.1, or [::1]
4 https://telemetry.swamp-club.com

Local spool

Every entry is written to disk before any send is attempted, in the spool under the config directory.

Each invocation is one file named telemetry-<YYYY-MM-DD>-<id>.json, where <id> is the event's properties.id. The file holds the event's properties object. The envelope, distinct_id, and $repo_id are added when the entry is sent.

File Removed
Sent entry Immediately
Sent entry, with telemetryKeepFlushed: true Renamed to .flushed.json, deleted after 2 days
Unsent entry After 7 days, by a CLI command that succeeds
Quarantined entry (.quarantined.json) After 30 days. Written by swamp serve.

Envelope

{
  "event": "cli_invocation",
  "distinct_id": "string",
  "insert_id": "string",
  "properties": {
    "id": "string",
    "invocation": {
      "command": "string",
      "subcommand": "string",
      "commandPath": ["string"],
      "args": ["string"],
      "optionKeys": ["string"],
      "globalOptions": ["string"]
    },
    "result": {
      "status": "success | error | user_error",
      "errorType": "string",
      "errorMessage": "string",
      "exitCode": 0
    },
    "startedAt": "ISO-8601",
    "completedAt": "ISO-8601",
    "durationMs": 0,
    "swampVersion": "string",
    "denoVersion": "string",
    "platform": "string",
    "invocationContext": {
      "configuredAiTools": ["string"],
      "detectedAiTool": "string",
      "agentSessionDetected": false,
      "isInteractive": false,
      "externalDatastoreConfigured": false,
      "datastoreType": "string",
      "externalVaultConfigured": false
    },
    "parentInvocationId": "string",
    "workflowContext": {
      "workflowName": "string",
      "runId": "string",
      "jobName": "string",
      "stepName": "string",
      "modelType": "string",
      "executor": "string"
    },
    "triggerSource": "schedule | webhook | api",
    "initiatedBy": "string",
    "$repo_id": "string"
  }
}

Optional fields appear when they have a value.

Top-level fields

Field Type Required Value
event string Yes Always cli_invocation.
distinct_id string Yes Random per-install UUID from <config>/identity.json. Falls back to $repo_id when identity.json is unavailable.
insert_id string Yes Random per-event UUID, equal to properties.id. The ingest service drops a repeated insert_id.
properties object Yes The fields below.

properties

Field Type Required Value
id string Yes Random per-event UUID.
invocation object Yes See invocation.
result object Yes See result.
startedAt string Yes ISO-8601 timestamp.
completedAt string Yes ISO-8601 timestamp.
durationMs number Yes Milliseconds between startedAt and completedAt.
swampVersion string Yes The CLI version, e.g. 20260930.201417.0-sha.69135e9d.
denoVersion string Yes The embedded Deno runtime version, e.g. 2.9.7.
platform string Yes Operating system: linux, darwin, or windows.
invocationContext object No See invocationContext.
parentInvocationId string No Present on workflow child events. The id of the swamp workflow run event.
workflowContext object No Present on workflow child events and swamp serve workflow runs. See workflowContext.
triggerSource string No Present on swamp serve runs. schedule, webhook, or api.
initiatedBy string No Present on swamp serve runs. The principal that started the run: user:<id>, service:<name>, worker:<name>, or ghost.
$repo_id string No Random per-repository UUID from repoId in .swamp.yaml. Present when the command resolved a repository.

invocation

Field Type Required Value
command string Yes First word of the command, e.g. model.
subcommand string No Second word of the command, e.g. method.
commandPath string[] No The resolved command from the CLI's own command tree, e.g. ["model","method","run"].
args string[] Yes Positional arguments after the command words, redacted as described in Redaction.
optionKeys string[] Yes Flag names as typed, e.g. --input. An undeclared flag is recorded as <UNKNOWN_OPTION>.
globalOptions string[] Yes Global flags used: --json, -q, --quiet, -v, --verbose, --no-telemetry, --no-color, --show-properties.

swamp serve model-method runs record subcommand as method run and args as [<model>, <method>]. CLI commands and workflow child events record subcommand as method and args as ["run", <model>, <method>].

result

Field Type Required Value
status string Yes success, user_error (the error is a user error), or error.
exitCode number Yes 0 on success, 1 on error.
errorType string No The error's class name, e.g. UserError.
errorMessage string No The first line of the error message, after redaction.

invocationContext

Field Type Required Source
configuredAiTools string[] No tools in .swamp.yaml. Present when the command resolved a repository.
detectedAiTool string No claude, cursor, kiro, opencode, codex, or pi, derived from the variables below.
agentSessionDetected boolean Yes Derived from the variables below.
isInteractive boolean Yes Whether stdin is a terminal.
externalDatastoreConfigured boolean Yes Whether datastore.type in .swamp.yaml is set to anything other than filesystem.
datastoreType string No datastore.type in .swamp.yaml.
externalVaultConfigured boolean Yes Whether any vault in the repository uses a provider other than local_encryption or mock.

detectedAiTool and agentSessionDetected are derived from whether these environment variables are set, and from their values: CLAUDECODE, CLAUDE_CODE_ENTRYPOINT, TERM_PROGRAM, CURSOR_TRACE_ID, AGENT_CONTEXT_OUT, OPENCODE, CODEX_SANDBOX_NETWORK_DISABLED, CODEX_SANDBOX, PI_CODING_AGENT, AGENT, AI_AGENT, IS_AGENT. The event carries the derived tool name and boolean.

workflowContext

Field Type Required Value
workflowName string Yes Name of the top-level workflow.
runId string Yes Random per-run UUID of the top-level run.
jobName string Yes Name of the job containing the step.
stepName string Yes Name of the step.
modelType string No Type of the model the step ran, e.g. command/shell.
executor string No Where the step ran: loopback or a worker name.

Identifiers

Every identifier in the envelope is a random UUID generated by Swamp.

Identifier Scope Stored in
distinct_id Per install <config>/identity.json
insert_id, properties.id Per event The event
$repo_id Per repository repoId in .swamp.yaml
runId Per workflow run The workflow run record
parentInvocationId Per invocation The event

What the ingest service adds

The ingest service stores each event's event, distinct_id, insert_id, and properties, a receive timestamp, and the account details below. When a request carries x-api-key, the service resolves the key and stores the result with each event.

Key type Stored with each event
Personal API key The account's username.
Collective API key distinct_id replaced by collective:<collective id>. The collective's id and slug, and its members' user ids and usernames, added to properties.
No key The username previously linked to the event's distinct_id, if a signed-in event from the same install has linked one.

Redaction

Telemetry keeps what kind of thing was done and what it was called. Names are labels, and Swamp sends them as typed. Paths, values, and queries over data are content, and Swamp redacts them before the event is built.

Kept as sent Redacted before the event is built
Command words at any depth (commandPath) Positional arguments outside the kept slots: <REDACTED>
Model, workflow, job, step, and vault names Flag values, including --input values
Model types and method names, including private extensions swamp vault put keys and values
Search queries swamp data query predicates
Flag names Everything after --
Error class, and the first line of the error after redaction Paths, home-directory usernames, and internal hostnames in error messages

Argument redaction

Swamp sends a positional argument when it fills one of these named slots in the command's definition: collective, data_name, definition_name, enabled, extension, grant_id, method_name, model_id_or_name, model_or_type, name, new_name, number, old_name, output_id, output_id_or_model_name, query, report_name, run_id_or_workflow, slug, step_name, token-id, type, vault_name, vault_name_or_id, version, workflow_id_or_name, workflow_name. The argument key is sent for swamp config commands.

Every other positional argument is replaced with <REDACTED>. A value typed into a kept slot is sent as typed, whatever it contains.

swamp help sends the words that follow it while they resolve to real commands. When the positional arguments fall outside the command's definition, Swamp redacts all of them.

Error message redaction

errorMessage is the first line of the error, rewritten in this order:

  1. Exact occurrences of the filesystem paths the error names, redacted argument values and their absolute forms, the working directory, $HOME, and the repository directory are replaced. A path-like value becomes <PATH>. Any other value becomes <REDACTED>.
  2. file:// URLs, quoted paths, and unquoted POSIX, ~/, Windows drive, and UNC paths become <PATH>. A trailing :line:col is kept.
  3. Remaining usernames in /Users/<name>, /home/<name>, and C:\Users\<name> become <REDACTED>.
  4. Hostnames ending in .internal, .local, .lan, .corp, .intranet, .private, or .home become <REDACTED-HOST>.

Workflow child events and swamp serve events apply steps 2 to 4.

An event contains exactly the fields described in Envelope.

Worked example

This command, run inside a repository, points --server at an unreachable internal host:

swamp model create command/shell other --server http://build01.corp:9090
Error: Could not connect to ws://build01.corp:9090/: NetworkError: failed to connect to WebSocket: dns error

It produces this event. UUIDs and timestamps vary.

{
  "event": "cli_invocation",
  "distinct_id": "cbd26c76-...",
  "insert_id": "1cc948c9-...",
  "properties": {
    "id": "1cc948c9-...",
    "invocation": {
      "command": "model",
      "args": ["command/shell", "other"],
      "optionKeys": ["--server"],
      "globalOptions": [],
      "subcommand": "create",
      "commandPath": ["model", "create"]
    },
    "result": {
      "status": "user_error",
      "exitCode": 1,
      "errorType": "UserError",
      "errorMessage": "Could not connect to ws://<REDACTED-HOST>:9090/: NetworkError: failed to connect to WebSocket: dns error"
    },
    "startedAt": "2026-09-30T22:26:26.132Z",
    "completedAt": "2026-09-30T22:26:26.172Z",
    "durationMs": 40,
    "swampVersion": "20260930.201417.0-sha.69135e9d",
    "denoVersion": "2.9.7",
    "platform": "linux",
    "invocationContext": {
      "agentSessionDetected": true,
      "isInteractive": false,
      "externalDatastoreConfigured": false,
      "externalVaultConfigured": false,
      "configuredAiTools": ["claude"],
      "detectedAiTool": "claude"
    },
    "$repo_id": "caec33e6-..."
  }
}

Swamp kept the model type and name, recorded the flag name --server, and wrote the host in the error as <REDACTED-HOST>.

Other commands, as recorded in args:

Command args
swamp vault put prod-secrets DB_PASSWORD hunter2 ["prod-secrets","<REDACTED>","<REDACTED>"]
swamp data query 'attributes.owner == "alice@example.com"' ["<REDACTED>"]
swamp extension search aws ["aws"]
swamp model method run my-model execute -- --secret=/etc/shadow ["run","my-model","execute","<REDACTED>"]

Verifying

To read exactly what is sent, point the endpoint at a local listener that answers 202, run any command, and read the request body:

SWAMP_TELEMETRY_ENDPOINT=http://127.0.0.1:8080 swamp model get my-model

To read each entry on disk before it is sent, point the endpoint at a closed loopback port. The entry stays in <config>/telemetry/:

SWAMP_TELEMETRY_ENDPOINT=http://127.0.0.1:1 swamp model get my-model

Opt-outs

swamp config set telemetry.collection disabled, telemetryDisabled: true in .swamp.yaml, SWAMP_NO_TELEMETRY, DO_NOT_TRACK, and --no-telemetry each turn usage telemetry off. See Opt-outs for the scope of each, and Turn Off Usage Telemetry for the steps.

Other outbound requests

Request When Turn off
Update check At most once per 24 hours, after a command succeeds, for non-JSON output, when automatic updates are off. A HEAD request to https://artifacts.swamp-club.com with a 5-second timeout. SWAMP_NO_UPDATE_CHECK=1
Extension auto-resolve When a command needs an extension type from a trusted collective that is missing locally. trustedCollectives: [] in .swamp.yaml, with trustMemberCollectives left false
whoami When SWAMP_API_KEY is set and the cached username or token scopes are missing. Unset SWAMP_API_KEY

SWAMP_NO_UPDATE_CHECK accepts the same values as SWAMP_NO_TELEMETRY. Extension auto-resolve runs inside a repository. See Trust Configuration.