Command Profiles
Command profiles restrict which commands dtctl exposes to a named subset. A
profile shapes every discovery surface at once — --help, the
dtctl commands catalog, and shell completion — and hard-blocks
invocation of anything outside the set.
The motivating use case is embedding dtctl in AI agents. When only a slice of the
CLI is relevant — say an investigation agent that needs query and Davis
analyzers but never auth login or the cloud-provisioning verbs — a large,
mostly-irrelevant command menu confuses the agent. A profile trims the surface to
what matters, from configuration alone. No fork, one binary.
Profiles are a convenience, not a security boundary. Like safety levels, they are client-side. A determined caller can unset
DTCTL_PROFILEor edit the config. For real restriction, scope the API token.
Quick start
Bind a built-in profile to a context so an embedded agent inherits the reduced surface with zero flags:
dtctl config set-context prod-agent \
--environment https://abc12345.apps.dynatrace.com \
--token-ref prod-token \
--profile query \
--safety-level readonly
# Now the agent only sees the query surface:
dtctl commands # catalog reflects the profile
dtctl --help # help reflects the profile
dtctl auth login # blocked with a clear error
Or select a profile for a single controlled environment via the environment variable, which takes precedence over any context binding:
DTCTL_PROFILE=query dtctl commands
Built-in profiles
| Profile | Surface (plus always-available commands) |
|---|---|
full |
Everything (the default; today’s behavior) |
query |
query, get analyzers, describe analyzer, exec analyzer, verify analyzer |
investigate |
query, logs, get, find, describe |
Profile names are deliberately topical, never permission words like
readonly — that axis belongs to safety levels (see below). If a profile should
also forbid writes, pair it with --safety-level readonly on the context.
Defining your own
Profiles live in the config file under a top-level profiles map. A profile is a
description plus a flat commands allowlist:
profiles:
triage:
description: Read-only incident triage for on-call agents
commands:
- query
- logs
- get slos # only SLOs from the get verb, not all of `get`
- describe
contexts:
- name: oncall-agent
context:
environment: https://abc12345.apps.dynatrace.com
token-ref: oncall-token
profile: triage
safety-level: readonly
Matching rules:
- Each
commandsentry is a command-path prefix: a verb (query), a resource (get workflows), or a full path. An entry matches a command when it equals or is a segment-prefix of that command’s path, so listing a parent verb (describe) includes its whole subtree. - Default-deny: anything not matched (and not always-available) is masked. There is no denylist — you always list what is allowed. Adding a new command to dtctl never silently widens an existing profile.
- To allow a parent but only some children, list the specific child paths
(
commands: [get analyzers, get slos]rather thancommands: [get]).
User-defined profiles take precedence over a built-in preset of the same name.
Always-available commands
Only two commands are allowed regardless of profile — the irreducible core that lets an agent discover its surface and a user get help:
commands(andcommands howto) — the machine-readable catalog agents bootstrap fromhelp
Everything else is subject to the allowlist, including config, ctx,
completion, and version. This is deliberate: config/ctx can rotate
credentials and switch environments, so a locked-down agent profile must be able
to withhold them. If a profile needs any of these, list it explicitly (e.g.
commands: [query, config]).
Selecting the active profile
Precedence, highest first:
DTCTL_PROFILE env > context-bound profile > none (= full)
DTCTL_PROFILE— for products that wrap the binary in a controlled environment.- Context binding (
--profileonset-context) — the recommended path for embedding; agents inherit the surface with zero flags. - None — the full command tree (default, fully backward compatible).
There is no global default-profile setting: a config-wide profile is a footgun (set once, forgotten, every invocation silently restricted). Activation is always explicit per-environment or scoped to a context.
What a blocked invocation looks like
$ DTCTL_PROFILE=query dtctl auth login
Error: command "auth login" is not available in profile "query"
This profile exposes a reduced command set. Run 'dtctl commands' to see
what is available, or unset DTCTL_PROFILE to use the full CLI.
In agent mode the same block is reported as a structured error
with code: "profile_blocked". The dtctl commands catalog also advertises the
active profile and safety_level so an agent can see both constraints at once.
Profiles vs. safety levels
Profiles and safety levels are orthogonal axes that compose on a context:
| Safety level | Profile | |
|---|---|---|
| Question | “What may this command do?” | “Which commands exist here?” |
| Axis | Permission / blast radius | Topic / surface |
| Effect on help & catalog | None — blocks at run time | Removes the command entirely |
A profile decides which commands are on the menu; the safety level decides what those commands are allowed to do.
Set both on a context to express, e.g., “this agent only sees query/Davis
analyzers and can never mutate.”