CLI

Manage configurations, fleets, and deployments from the terminal.

The Telflo CLI, telflo, manages Telflo from a terminal, a CI pipeline, or an AI coding agent. It works on the same objects as the web app: a configuration you push from a script opens in the editor, and a version you publish in the editor deploys from a CI job. Nothing is exported or synchronized between the two.

Where the CLI sits: terminal, CI, and agents reach the same control planeCLIENTSYour terminaltelflo loginCI pipelineTELFLO_API_TOKENAI coding agenteither credentialTELFLOOne organizationConfigurations and versionsFleets and deploy groupsValidation and the test sandboxThe web app works on the same objects.configurationstatusYOUR COLLECTORSus-eastIn synceu-westIn syncap-southIn syncCollectors pull; nothing is pushed to a host.
The CLI is another client of the same control plane. A terminal signed in as you, a CI pipeline holding an organization API token, and an AI coding agent all reach one organization's configurations and fleets. Collectors still pull their configuration and report their status back.

Install

The CLI is published to npm as telflo and requires Node.js 20 or newer.

npm install -g telflo

Confirm the install and print the CLI's version:

telflo --cli-version

To run it without installing, prefix any command with npx, such as npx telflo config list.

The flag is --cli-version, or -V, rather than --version. --version <n> is taken: on config pull, config rules, config publish, deploy, and test it selects a configuration version number. Running telflo --version is a usage error.

Sign in

The CLI acts on one organization, with one of two credentials.

CredentialActs asUse for
Session, from telflo loginYouWorking interactively at a terminal.
Organization API token, from Settings > API TokensA service accountCI pipelines, automation, and AI coding agents.

Sign in as yourself

telflo login

Your browser opens Telflo, you select the organization to work in, and you approve the session. The CLI then acts as you: configurations you create or change appear in the web app immediately, exactly as if you had made the change there. To switch organizations, run telflo login again.

The approval page hands the session back to the waiting command on your machine. When it cannot, because the browser blocks the request or the command is no longer listening, the page shows a one-time code instead; paste it into the terminal and press Enter. On a remote or SSH host, where the browser is on a different machine, skip the hand-off:

telflo login --no-browser

The command prints a URL to open anywhere and waits for the code. To finish a sign-in with a code you already copied, pass it directly:

telflo login --code <code>

A sign-in waits five minutes and then gives up.

The one-time code is a credential in its own right: it completes a sign-in to your organization. Paste it only into the terminal that asked for it, and keep it out of chat, tickets, and logs.

Sign in with an organization API token

An organization admin creates the token in Settings > API Tokens. See API tokens.

Choose the access the pipeline needs. Read covers reading configurations, flows, and fleets, and running validate and ottl lint. Read & Write is required by config push, config create, config publish, deploy, fleet create, fleet install, and test. A test run is a write because it records a run against the configuration.

Set the token in the environment; no login step is needed.

export TELFLO_API_TOKEN=tf_...

A token belongs to the organization rather than to a person, so changes made with it are attributed to the token's service account. You can also store a token on the machine instead of setting it on every invocation:

telflo login --token tf_...

Confirm which credential is in use

telflo whoami

The command reports the organization the CLI acts on and whether it acts as a user or as a service account. For a user it names your email address; for a service account it names the token and its access. Run it first when a command fails with an authentication error, to confirm which credential the CLI actually picked up.

Credentials and environment

Credentials are stored in ~/.telflo/credentials.json, written readable by your user only. telflo logout deletes that file. Logging out is local and revokes nothing: to withdraw a token's access, delete the token in Settings > API Tokens.

The CLI reads four environment variables.

VariablePurposeDefault
TELFLO_API_TOKENThe organization API token to authenticate with.None
TELFLO_API_URLThe API the CLI calls.https://telflo.com
TELFLO_APP_URLThe app telflo login opens in the browser.https://telflo.com
TELFLO_CONFIG_DIRThe directory holding credentials.json.~/.telflo

The token and the API URL are resolved in the same order: a command-line flag, then the environment variable, then the stored credentials, then the default. TELFLO_APP_URL applies to login only, and TELFLO_CONFIG_DIR is read from the environment alone.

--json is the only flag accepted anywhere in the command line. --token and --api-url belong to the commands that call the API and must be written after the subcommand, as in telflo config list --token tf_.... Write every flag after the subcommand and it is always in the right place.

Two precedence details are worth knowing:

  • A token found in the environment takes priority over a stored session, so TELFLO_API_TOKEN set in a shell where you are also signed in makes the CLI act as the token's service account rather than as you.
  • When the CLI is signed in as you, it calls the API recorded with that session. --api-url and TELFLO_API_URL redirect a token credential, not a session.

telflo whoami reports which credential is in effect.

The core loop

Editing a configuration from the terminal follows the same path as the editor: pull the current YAML, change it, prove it, then roll it out.

The CLI loop: pull, edit, validate, push, test, deploy1Pulltelflo config pull2Editin your editor3Validatetelflo validate4Pushtelflo config push5Testtelflo test6Deploytelflo deploy
The terminal equivalent of the build, test, publish, and deploy path. Each stage is a single command, and each one exits non-zero when it fails, so the loop runs unattended in CI.
telflo config list                            # find the configuration id
telflo config pull <id> -o config.yaml        # its latest YAML, and its collector pin
telflo validate config.yaml --config <id>     # check it on a real collector
telflo config push <id> -f config.yaml --publish
telflo test --spec test.yaml --config <id>    # run sample telemetry through it
telflo deploy --fleet <fleet-id> --config <id>

Every command accepts --json for machine-readable output, and telflo <command> --help lists its full set of flags.

CommandPurpose
telflo login · logout · whoamiSign in, sign out, and show which credential is in use.
telflo validate <file>Validate collector YAML against a real collector binary.
telflo ottl lint <file>Parse-check the OTTL expressions in a configuration.
telflo config list · get · pullBrowse configurations and their versions.
telflo config create · push · publishCreate a configuration, save the next version, and publish it.
telflo config rules <id>Show the validation rules saved on a version.
telflo test --spec <spec>Run a pipeline test in the Telflo sandbox.
telflo flows list · get · pullBrowse flows and take their YAML.
telflo fleet list · create · deploymentsManage fleets and their deploy groups.
telflo fleet install <id>Print the command that installs a collector into a fleet.
telflo deployRoll a published version out to a fleet.
telflo skills installInstall the Telflo skill for AI coding agents.

Collector versions

Each configuration version is pinned to a collector distribution and version, the same pin the editor's toolbar shows. Anything that targets a stored configuration runs on that pin automatically, so a check from the terminal uses the collector the configuration actually runs on.

When you validate or test a local file, pass --config <id> so the file is checked on that configuration's collector rather than the server's default. --collector-version and --distribution override either.

The collector is resolved in this order:

  1. --collector-version and --distribution flags.
  2. collector_version and distribution in a test spec.
  3. The pin on the stored configuration version.
  4. The server's default collector.

One case departs from that order. When telflo test runs a local file against a stored configuration, with -f and --config together, the configuration's pin takes precedence over the spec's fields, because the point of that combination is to run the file on the collector the configuration uses. Pass --collector-version or --distribution to override it.

telflo config pull reports the pin it handed you, such as pinned to otelcol-contrib 0.147.0, so a local file can always be traced back to a collector.

Validate a configuration

Validation runs the YAML through a real collector binary and reports what the collector rejects. It answers whether a configuration is well-formed and schema-valid, not what it does to telemetry. See Validation.

telflo validate config.yaml
telflo validate config.yaml --config <id>
telflo validate config.yaml --collector-version 0.147.0

validate is the one command that works without signing in. Without a credential it uses the public validator, which is rate-limited per IP address and answers deliberately slowly; the pause is not the product being slow. With a credential it uses your organization's validator, and reports the full text when something goes wrong, so use a credential in a pipeline.

The file argument accepts - to read YAML from standard input. Findings are printed to standard output and the failure line to standard error, so redirecting standard output keeps the report. With --json, the result reports whether the configuration is valid and lists each error with the component path it came from.

An invalid configuration exits 4. A failure of the validation service itself exits 1 with a message stating that the YAML could not be validated; in that case nothing was checked, and the configuration is neither valid nor invalid.

A collector version the validator does not have is reported as an invalid configuration rather than as a bad flag, with the error Collector version <v> is not supported. Check the value passed to --collector-version before reading that as a problem with your YAML.

Lint OTTL expressions

OTTL is the expression language inside the transform, filter, routing, and tail_sampling processors. An OTTL mistake is a common reason a configuration validates but does nothing, because the expression parses as valid YAML and only fails at runtime. telflo ottl lint parses every expression with the OTTL grammar and reports the YAML location of each problem.

telflo ottl lint config.yaml
telflo ottl lint --context log --expr 'severity_number >= 17'
telflo ottl lint rules.txt --context span --kind statement

Without --context, the file is read as a collector configuration and every OTTL expression in it is checked. With --context, the --expr values and the file's non-blank lines are read as raw expressions of the kind given by --kind, which is condition, statement, or value. --kind applies to raw expressions only; it has no effect when a configuration is linted.

The linter also reports single-escaped regular expression sequences, such as "\d", which parse without error but never match. Write them with a double backslash.

Two results are not failures and are worth reading:

  • A small set of transform-processor functions, such as convert_sum_to_gauge, are reported as notes rather than errors. Other processor-specific functions are still reported as errors, because the linter registers the standard OTTL function set.
  • Expressions the linter cannot judge are listed as skipped. A configuration whose expressions are all skipped exits 0 without anything having been checked, so read the skipped list rather than the exit code alone.

An invalid expression exits 4. Unlike validate, ottl lint always requires a credential.

Configurations

telflo config covers the configuration list and its version history.

CommandPurpose
telflo config listThe configurations the credential can see, with their latest and published version numbers.
telflo config get <id>One configuration and its version history, newest first.
telflo config pull <id> [--version <n>] [-o <file>]The YAML of a version, to standard output or a file.
telflo config create --name <name> -f <file>A new configuration, created as draft version 1.
telflo config push <id> -f <file> [--publish]Save the YAML: into the latest draft, or as a new version when the latest is published.
telflo config publish <id> --version <n>Publish a version. Publishing again is a no-op.
telflo config rules <id> [--version <n>]The validation rules saved on a version. Without --version, the latest version, which may be a draft.

A credential sees its own configurations, draft and published, plus every published configuration in the organization. Another member's unpublished draft is not listed, and a draft created with an API token belongs to that token's service account, so your members do not see it until it is published.

-f accepts - to read from standard input. Without -o, config pull writes the YAML to standard output and its informational line to standard error, so it pipes cleanly into another tool:

telflo config pull <id> | yq '.processors'

How versions behave

Versions are numbered by the server, and a published version is immutable. telflo config push behaves accordingly:

  • When the latest version is a draft, push edits it in place.
  • When the latest version is published, push appends a new version.

Read the resulting number from push's output rather than assuming it; with --json, the result reports whether the version was patched or appended, and its number. Push validates before it saves, so an invalid configuration exits 4 and nothing is written. See Versions & publishing.

Flows

A flow is a prebuilt collector configuration from Telflo's curated catalog. The CLI reads the same catalog as telflo.com/flows.

telflo flows list
telflo flows get otel-to-clickstack
telflo flows pull otel-to-clickstack -o config.yaml
telflo config create --name "OTel to ClickStack" -f config.yaml

flows get returns the flow's description and its YAML; flows pull returns the YAML alone, with the collector version and distribution the flow was validated against. Creating a configuration from the pulled YAML gives you an independent copy, which you then edit, test, and deploy like any configuration.

config create does not carry a flow's collector pin. A configuration created from pulled YAML is pinned to the default distribution rather than the one the flow was validated against, and a configuration's distribution cannot be changed afterwards. When a flow's pin matters, use Use this flow in the app instead, which preserves it. See Flows.

Pipeline tests

A test runs sample telemetry through one of the configuration's pipelines on a real collector in the Telflo sandbox, then checks the output against rules you write. It answers what the configuration does to data, which validation cannot. See Testing.

A test spec is a YAML file naming the pipeline to exercise, the sample to replay through it, and the assertions to make about the output.

test.yaml
signal: logs
pipeline: logs/in
duration: 5
sample: ./sample.json
rules:
  - name: card numbers are redacted
    context: log
    quantifier: none
    condition: IsMatch(String(body), "[0-9]{4}[ -][0-9]{4}[ -][0-9]{4}[ -][0-9]{4}")
FieldRequiredDescription
signalYestraces, metrics, or logs.
pipelineYesThe pipeline to run, as its service.pipelines key, such as logs/in.
sampleOne of the twoA path to a sample file, resolved relative to the spec file.
sample_textOne of the twoThe sample inline, as a string.
durationNoRun length in seconds, an integer from 1 to 60. Defaults to 5.
nameNoA name for the test, up to 200 characters.
collector_versionNoOverrides the configuration's pinned collector version.
distributionNoOverrides the configuration's pinned distribution.
rulesYes1 to 20 assertions about the output.

A sample is limited to 1 MB, whether it is read from a file or given inline.

Each rule reads a context, such as log, span, spanevent, metric, datapoint, resource, or scope, and applies a quantifier over the records in it.

QuantifierPasses whenAlso requires
allEvery record satisfies condition.condition
anyAt least one record satisfies condition.condition
noneNo record satisfies condition.condition
countThe number of matching records meets count.condition and count
distinctThe number of distinct values of expression meets count.expression and count

A count is an operator and a value, such as { operator: eq, value: 0 }. The operators are eq, ne, gt, gte, lt, lte, and between, which takes a second value. The distinct quantifier is the cardinality guardrail: it counts the distinct values an expression resolves to, so a high-cardinality attribute can be asserted against a budget.

Assert on behavior rather than on the literal contents of your sample, so a test keeps its meaning when the sample changes. Two rules are rejected before a run starts, as errors rather than as failures:

  • A condition that can never fail, such as true with the all quantifier. true is accepted with any, none, and count, where it asserts that records exist, that none do, or how many there are.
  • A single-escaped regular expression sequence, such as \d. Write \\d.

An all or none rule over a context with no records passes, because there is nothing to contradict it. The result carries an advisory saying so; read it rather than treating the pass as evidence.

Run a test

telflo test --spec test.yaml --config <id>          # a stored version
telflo test --spec test.yaml -f config.yaml         # a local file
telflo test --spec test.yaml -f config.yaml --config <id>   # a local file, on that config's collector

--config runs the configuration's latest version, which can be an unpublished draft. Pass --version <n> to pin the run to a specific version.

The run is submitted to the sandbox and polled until it reaches a terminal state, waiting up to the spec's duration plus two minutes. Failing assertions exit 4. A run that could not complete, because the sandbox rejected the configuration, a rule was refused, or the run timed out, exits 1. Read --json output to tell the two apart: it reports the run's status and each rule's result, and it is written to standard output even when the command exits non-zero.

Two flags connect a spec to the Validation tab of the configuration in the web app. Both require --config and neither can be combined with -f:

  • --save writes the spec's rules to the version's validation pipeline, then runs them together with the rules already saved there. Rules a person authored in the app are not replaced. The save happens first, so a rejected spec leaves no run behind. The spec's signal must match the pipeline it names, which a plain run does not require.
  • --saved runs the rules already saved in the app, so the spec's own rules are optional and any it carries are ignored.

Two limits shape what a run can prove. The sandbox runs only the pipeline named in the spec, and connectors are removed, so a test cannot assert what a connector does or how two pipelines interact. Organization variables are not substituted either: replace any $orgvar: reference with a real value before testing. See Vault.

The request carrying the spec and the configuration is limited to 1 MB. Exceeding it is reported as a usage error before anything is submitted.

Fleets and collectors

A fleet is a named group of collectors managed together, and a fleet's collectors are organized into deploy groups. The CLI calls a deploy group a deployment; it is the same object the Deploy Groups tab shows. See Fleet management.

telflo fleet list
telflo fleet create --label prod --type server-supervisor
telflo fleet deployments <fleet-id>

A fleet is either a server-supervisor fleet, for collectors on Linux hosts and in containers, or a kubernetes-supervisor fleet, for collectors in a cluster. --expires-in sets the connection token's expiry, as 180d, 1y, 2y, or never, and defaults to never. Telflo recommends a bounded expiry for long-lived fleets, so an exposed token cannot be used indefinitely. See Tokens & security.

A server fleet is created with a Default deploy group and is ready to install into. A Kubernetes fleet is created with none, so create one with telflo deploy before installing:

telflo deploy --fleet <fleet-id> --config <config-id> --name agents --role node

telflo fleet install prints the command that brings a collector online in a fleet, the same command the Install collectors dialog generates.

telflo fleet install <fleet-id> --platform docker --server-name web-1
telflo fleet install <fleet-id> --platform linux --arch amd64
telflo fleet install <fleet-id> --platform kubernetes --deployment <id>

The command itself goes to standard output and its explanatory notes to standard error, so redirecting standard output captures the command and nothing else. Read it before running it: on docker and kubernetes, an environment variable the configuration needs that you did not pass with --env is written into the command as a <value> placeholder, and running it unedited installs a collector with that literal string. The linux install leaves such a variable out and warns instead.

The docker and linux platforms require a server fleet, and kubernetes requires a Kubernetes fleet. Asking for the wrong pairing is refused by the API. A server fleet installs into its Default deploy group unless --deployment names another; a Kubernetes fleet needs exactly one deploy group, or --deployment to choose between several.

Flags that do not apply to the chosen platform are ignored rather than rejected: --server-name applies to docker, and --arch, --root-path, and --no-systemd apply to linux. The linux install downloads a bundle through a link that expires after 24 hours, so generate a fresh command for each host. --no-systemd runs the supervisor in the foreground instead of registering it as a service. Values passed with --env are written into the bundle; anything the configuration reads that you did not supply must be set on the host yourself.

The output is a credential. For docker and linux the printed command carries the fleet's connection token; for kubernetes it carries a link that serves the fleet's values for 24 hours. Keep the output out of shared logs, CI job output, and source control. See Tokens & security.

Deploy to a fleet

telflo deploy --fleet <fleet-id> --config <config-id>

Deploying assigns a published version of a configuration to a deploy group, and the group's collectors pull it and apply it. Without --version, the configuration's currently published version is used; a configuration with nothing published exits 4, so publish first with telflo config push --publish or telflo config publish.

When the fleet already has exactly one deploy group serving that configuration, deploy updates it. Otherwise, pass --name to create a group or --deployment <id> to choose an existing one. --deployment reassigns whatever that group was serving, so confirm the id with telflo fleet deployments first.

--replicas, --namespace, and --role apply to Kubernetes fleets and take effect when a group is created. A group's role is fixed once it exists; passing --role on an update has no effect. With --json, the result reports whether a group was created or updated, and the version number it now serves.

Creating a deploy group is the only group management the CLI does. Renaming and deleting groups are done in the app.

Rolling back is the same command with an earlier published version. Collectors converge on it the same way. See Update the configuration.

Output and exit codes

Human output is written for reading; --json output is written for parsing. In --json mode, standard output carries a single JSON document and every diagnostic goes to standard error, so a pipeline can read standard output without filtering. --json is never inferred, so pass it explicitly in a script. A failing command still writes its result document to standard output, which is where a test run's rule results are found on exit 4.

One case has no JSON at all: a malformed command line, such as an unknown flag, is rejected before --json is applied and is reported as plain text. Branch on the exit code rather than on standard error being parseable.

CodeMeaning
0The command succeeded.
1The API returned an error, or a run could not complete.
2The command was used incorrectly, such as an unknown flag or an invalid test spec.
3Authentication failed. The credential is missing, expired, revoked, or read-only.
4A check failed: the YAML is invalid, an OTTL expression is invalid, or a test assertion did not pass.

Exit code 4 is a verdict about your configuration. Exit codes 1 and 3 are about the request, and mean the configuration was not judged. Branch on the code rather than on message text.

Continuous integration

The CLI is designed to run unattended. Every command apart from login is non-interactive, takes its credential from the environment, and reports its result through the exit code. Nothing asks for confirmation, including config push --publish and deploy, and output is plain text with no colour to strip.

Use a token in CI rather than telflo login. The browser flow waits for a person, times out after five minutes, and does not read a piped code, so it cannot complete in a job.

  1. Create an organization API token in Settings > API Tokens. A Read token covers a pipeline that only validates and lints; anything that pushes, publishes, deploys, or runs a test needs Read & Write.
  2. Store it as a secret in your CI system and expose it to the job as TELFLO_API_TOKEN.
  3. Install the CLI in the job with npm install -g telflo, or call it through npx telflo.
  4. Run the checks you want to gate on. telflo validate, telflo ottl lint, and telflo test all exit 4 on a failure, which fails the job.

Requests are limited to 60 per minute for each token. telflo test spaces out its polling to stay inside that limit; a job that loops over many configurations should pace itself the same way.

The CLI calls the API directly and has no proxy setting, so a runner behind an egress proxy needs the Telflo API reachable without one. Requests carry no timeout of their own either, apart from a test run's own deadline: give the job a timeout so a stalled connection cannot hold a runner open.

A typical pipeline validates and lints the configuration on every change, runs the pipeline tests, and pushes and publishes only from the default branch. Keep deploy as a separate, manually approved step so a merge cannot roll a configuration out on its own.

Telflo recommends one token per pipeline, named for that pipeline, so a retired or compromised pipeline can be cut off by deleting its token without affecting the others.

AI coding agents

The CLI ships with a skill that teaches an AI coding agent how to use it: the loop through validate, push, test, and deploy, how to write a test spec, and how to read a result.

telflo skills install            # into ./.claude/skills/telflo-cli
telflo skills install --global   # into ~/.claude/skills/telflo-cli

The command copies files and makes no network call, so it needs no credential. It replaces the installed skill rather than merging into it, so run it again to take a newer version and expect local edits to that directory to be lost. The project install writes into the current directory, not the repository root.

Give the agent a Read & Write token through TELFLO_API_TOKEN, or run telflo login yourself first so the agent acts as you and its drafts appear in your editor. A read-only token fails every write with an authentication error.

This is separate from the AI assistant inside the Telflo editor. The assistant is Telflo's own feature: it builds and explains configurations on the canvas, is enabled in Settings > AI, and is metered in AI tokens. The skill is for an agent running on your machine, driving the CLI with your credential, and is limited only by that credential's access.

Troubleshooting

SymptomMeaningResolution
Exit 3, "Not logged in"No credential was found.Run telflo login, or set TELFLO_API_TOKEN.
Exit 3 on a write commandThe token is read-only, expired, or revoked.Check telflo whoami, then use a Read & Write token.
The CLI acts as the wrong identityTELFLO_API_TOKEN is set in the shell and takes precedence over your session.Unset the variable, or confirm with telflo whoami.
Exit 4 on validateThe collector rejected the YAML.Read the reported errors and their component paths.
Exit 4, "Collector version is not supported"The collector version does not exist, rather than the YAML being wrong.Correct --collector-version, or drop it to use the configuration's pin.
Exit 1, "could not validate"The validation service was unavailable.Nothing was checked. Retry; the YAML is not known to be invalid.
ottl lint reports nothing and exits 0The configuration has no OTTL, or every expression was skipped.Read the skipped list; a clean exit is not proof the expressions were checked.
Exit 4 on testAn assertion did not pass.Read the rule results in --json output.
Exit 1 on testThe run did not complete, or a rule was refused before it ran.Read the message and the run's stderr in --json output; neither is an assertion failure.
A test passed but proved nothingAn all or none rule matched a context with no records.Read the advisory on the result, and add a rule that asserts records exist.
Exit 4 on deployThe configuration has no published version, and none was named.Publish first with telflo config push --publish.
Exit 1 on deploy with --versionThat version exists but is not published.Publish it, or name a published version.
Exit 1 on fleet installThe platform does not match the fleet's type, or a Kubernetes fleet has no deploy group.Match the platform to the fleet, and create a group with telflo deploy --name.
A pushed version did not appearPush edited the existing draft in place instead of appending.Read the version number and the action from push's output.

For problems in the app rather than the CLI, see Troubleshooting.

Next steps

Last updated on

On this page