You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(slos): correct the documented upsert key, drop foreign id from POST body
Addresses six pre-merge review findings on #207.
1. The docs taught `dash0.com/id` as the SLO upsert key while `ImportSLO` is
origin-first, and SLO ids are server-assigned (`slo_<ulid>`) so the pinned
example id could never exist. The identifier table, the SLO YAML example,
the `apply` section, and the "notification channels and spam filters are the
two exceptions" sentence (in both docs/commands.md and the skill's SKILL.md)
now say origin — modeled on the existing `Dash0Team` row, which describes
the same routing.
2. `apply --dry-run` printed no identifier for an origin-only SLO document (the
recommended form). Added the origin fallback both origin-keyed baselines
already have.
3. `slos get <origin>` built the deep link and the id label from the raw
argument, producing a URL that does not resolve. Both are now read off the
response, mirroring `notificationchannels/get.go`.
4. `StripSLOServerFields` does not clear `dash0.com/id`, so the cross-org POST
fallback (id branch, preflight 404) sent the source org's id. `ImportSLO`
now calls the previously-unused `dash0api.ClearSLOID` on that path, and the
regression test asserts the POST body carries no id. Also deleted the
`ImportSLO` doc comment sentence claiming the strip clears the id.
5. There was no `slos` topic in the Agent Skill bundle: added the
`ManifestEntry`, the generator `topicSpec`, the SKILL.md topic-index row,
and regenerated, so `dash0 skill show slos` resolves.
6. `slos list -o wide`/`-o csv` were missing the `ORIGIN` column the shared
format contract promises, and `slos get` printed no `Origin:` line. Since
origin is the SLO upsert key, it is the field users script against.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/commands.md
+13-4Lines changed: 13 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -730,15 +730,16 @@ The identifier field location varies by asset kind:
730
730
|`PrometheusRule` (alerting rules) |`metadata.labels["dash0.com/id"]`| The CRD-level label is applied to every alerting rule converted from the CRD, so a CRD with multiple alerts shares one identifier — pin a unique label per CRD, or split multi-alert CRDs into one CRD per alert |
|`SLO`|`metadata.labels["dash0.com/origin"]` (`metadata.labels["dash0.com/id"]`when origin is absent) | OpenSLO v1 document (`apiVersion: openslo.com/v1`). SLO IDs are server-assigned (`slo_<ulid>`), so `dash0.com/origin` is the only client-settable upsert key and the recommended one. `dash0.com/origin` is preferred and upserts by that origin (PUT). When only `dash0.com/id` is present the CLI preflights `GET /api/slos/{id}`: on hit it PUTs (idempotent update — this is what makes reapplying a YAML downloaded from the Dash0 platform UI a no-op), on 404 it falls back to POST so cross-org apply stays idempotent, and other errors surface. A document with neither label creates a new SLO on every apply|
734
734
|`View`|`metadata.labels["dash0.com/id"]`||
735
735
|`Dash0SpamFilter` (v1alpha1 and v1alpha2) |`metadata.labels["dash0.com/id"]`|`metadata.labels["dash0.com/origin"]` is preferred over the ID when both are present; an ID-only filter is not fully idempotent because the server reassigns the ID on the first PUT |
736
736
|`Dash0NotificationChannel`|`metadata.labels["dash0.com/origin"]`| There is no user-settable ID field for notification channels — the origin label is the upsert key. A document without it creates a new channel on every apply |
737
737
|`Dash0Team`|`metadata.labels["dash0.com/origin"]` (`metadata.labels["dash0.com/id"]` when origin is absent) | Organization-level. `dash0.com/origin` is preferred and upserts by that origin (PUT). When only `dash0.com/id` is present the CLI preflights `GET /api/teams/{id}`: on hit it PUTs (idempotent update — this is what makes reapplying a YAML downloaded from the Dash0 platform UI a no-op), on 404 it falls back to POST so cross-org apply stays idempotent, and other errors surface. A document with neither label creates a new team on every apply. `spec.members` accepts email addresses or internal member ids interchangeably |
738
738
739
739
The `dash0.com/id` label is the user-defined external identifier and is distinct from `dash0.com/origin`, which records the system of record (`dash0-cli`, `terraform`, `ui`).
740
740
The CLI strips `dash0.com/origin` from outbound payloads for the asset types where the server treats origin as provenance metadata (dashboards, views, check rules, synthetic checks), so do not use origin as the upsert key for those kinds.
741
-
Notification channels and spam filters are the two exceptions: their server APIs key on origin, and the CLI preserves it accordingly.
741
+
Notification channels, spam filters, SLOs, and teams are the exceptions: their server APIs key on origin, so the CLI reads the origin label off the document and uses it as the upsert key in the request path (`PUT /api/notification-channels/{origin}`, `PUT /api/spam-filters/{origin}`, `PUT /api/slos/{origin}`, `PUT /api/teams/{origin}`).
742
+
For SLOs origin is not merely accepted, it is the recommended key: the server assigns SLO IDs (`slo_<ulid>`), so a hand-written document can only be made idempotent through `dash0.com/origin`.
742
743
743
744
When `list -o yaml` or `get -o yaml` exports an existing asset, the server-assigned ID is rendered into the correct field, so the export-edit-reapply workflow round-trips through the identifier automatically.
744
745
@@ -783,6 +784,13 @@ Exported definitions never carry the field (`get`/`list` omit it from `-o yaml`/
783
784
The CLI warns when an applied document carries a non-empty `spec.routing.assets`.
784
785
To bind a check rule, set the `dash0.com/notification-channel-ids` annotation on the check rule; to bind a synthetic check, set`spec.notifications.channels` on the synthetic check.
785
786
787
+
`SLO` documents use the same upsert-key selection as `Dash0Team`, and for SLOs `dash0.com/origin` is the recommended key rather than merely the preferred one.
788
+
SLO IDs are assigned by the server (`slo_<ulid>`), so `dash0.com/id` cannot be chosen up front — only `dash0.com/origin` can.
789
+
`dash0.com/origin` wins when present and PUTs unconditionally (create-or-replace at that origin).
790
+
When only `dash0.com/id` is present the CLI preflights the SLO with a GET — on hit it PUTs (idempotent update, the path that makes a UI-downloaded YAML reapply cleanly), on 404 it falls back to POST so a YAML from one organization applies to another as a fresh create, and other preflight errors surface instead of silently creating a duplicate.
791
+
On that POST fallback the foreign `dash0.com/id` is removed from the request body, since the server assigns the ID.
792
+
A document with neither label creates a new SLO on every apply.
793
+
786
794
`Dash0Team` documents are dispatched to the organization-level teams endpoint (also not associated with a dataset).
787
795
Upsert key selection: `dash0.com/origin` wins when present and PUTs unconditionally.
788
796
When only `dash0.com/id` is present the CLI preflights the team with a GET — on hit it PUTs (idempotent update, the path that makes a UI-downloaded YAML reapply cleanly), on 404 it falls back to POST so a YAML from one organization applies to another as a fresh create, and other preflight errors surface instead of silently creating a duplicate.
@@ -904,15 +912,16 @@ spec:
904
912
interval: 60s
905
913
```
906
914
907
-
SLO (OpenSLO v1 format):
915
+
SLO (OpenSLO v1 format).
916
+
The upsert key is `dash0.com/origin`, not `dash0.com/id` — SLO IDs are server-assigned, so `dash0.com/origin` is the label to pin in version control:
Copy file name to clipboardExpand all lines: internal/skill/content/SKILL.md
+8-5Lines changed: 8 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,13 +1,13 @@
1
1
---
2
2
name: dash0-cli
3
-
description: Use when working with Dash0 observability data or configuration via the dash0 CLI (the `dash0` binary) — querying logs, spans, traces, metrics, or failed checks; managing dashboards, views, check rules, synthetic checks, recording rules, notification channels, or spam filters; sending OTLP telemetry or deployment events; or managing teams, members, and profiles. Trigger on "Dash0", "dash0 CLI", or any of these operations.
3
+
description: Use when working with Dash0 observability data or configuration via the dash0 CLI (the `dash0` binary) — querying logs, spans, traces, metrics, or failed checks; managing dashboards, views, check rules, synthetic checks, SLOs, recording rules, notification channels, or spam filters; sending OTLP telemetry or deployment events; or managing teams, members, and profiles. Trigger on "Dash0", "dash0 CLI", or any of these operations.
4
4
---
5
5
6
6
<!-- This file is packaged with the dash0-cli distribution. Installed copies are overwritten by `dash0 skill install` / `make skill-bundle`; edit the hand-curated source at internal/skill/content/SKILL.md in the dash0hq/dash0-cli repository instead. -->
7
7
8
8
# dash0-cli
9
9
10
-
`dash0` is a command-line interface for the [Dash0](https://www.dash0.com) observability platform. It manages Dash0 assets (dashboards, views, check rules, synthetic checks, recording rules, notification channels, spam filters), queries telemetry (logs, spans, traces, metrics, failed checks), sends telemetry via OTLP, and manages organization entities (teams, members, profiles).
10
+
`dash0` is a command-line interface for the [Dash0](https://www.dash0.com) observability platform. It manages Dash0 assets (dashboards, views, check rules, synthetic checks, SLOs, recording rules, notification channels, spam filters), queries telemetry (logs, spans, traces, metrics, failed checks), sends telemetry via OTLP, and manages organization entities (teams, members, profiles).
11
11
12
12
**Prefer `dash0 --agent-mode <command> --help` over guessing flags.** Every command's exact, always-current flag list, aliases, and examples are available as structured JSON via `--agent-mode <command> --help` (e.g. `dash0 --agent-mode dashboards list --help`). This bundle deliberately does not duplicate flag tables — they'd go stale the moment a flag is added or renamed. Use the topics below for concepts, YAML formats, and workflows that `--help` output can't express; use `--agent-mode <command> --help` for the exact flags to pass.
13
13
@@ -17,7 +17,7 @@ description: Use when working with Dash0 observability data or configuration via
|`SLO`|`metadata.labels["dash0.com/origin"]` — SLO IDs are server-assigned (`slo_<ulid>`), so origin is the only client-settable key; `metadata.labels["dash0.com/id"]` is used only when origin is absent |
58
59
|`Dash0SpamFilter`|`metadata.labels["dash0.com/id"]` (`dash0.com/origin` takes precedence when both are present) |
59
60
|`Dash0NotificationChannel`| no ID field — `metadata.labels["dash0.com/origin"]` is the upsert key |
61
+
|`Dash0Team`|`metadata.labels["dash0.com/origin"]` (`metadata.labels["dash0.com/id"]` when origin is absent) |
60
62
61
-
**Origin vs ID — do not conflate them.***Origin* (`dash0.com/origin` label) identifies which system is the authoritative source of truth for an asset (`dash0-cli`, `terraform`, `ui`) — it's provenance metadata, not a lookup key, and the CLI strips it before sending most asset types to the API so it doesn't claim ownership of assets managed elsewhere. *ID* is the user-defined external identifier used for upsert, described above. Notification channels and spam filtersare the two exceptions where origin (not ID) is the upsert key.
63
+
**Origin vs ID — do not conflate them.***Origin* (`dash0.com/origin` label) identifies which system is the authoritative source of truth for an asset (`dash0-cli`, `terraform`, `ui`) — it's provenance metadata, not a lookup key, and the CLI strips it before sending most asset types to the API so it doesn't claim ownership of assets managed elsewhere. *ID* is the user-defined external identifier used for upsert, described above. Notification channels, spam filters, SLOs, and teams are the exceptions where origin (not ID) is the upsert key — for those kinds the CLI reads the origin label off the document and upserts against it (`PUT /api/<kind>/{origin}`). For SLOs origin is not just accepted but recommended: the server assigns SLO IDs, so `dash0.com/origin` is the only way to make a hand-written SLO document idempotent.
62
64
63
65
When `list -o yaml` or `get -o yaml` exports an existing asset, the server-assigned ID is rendered into the correct field, so an export → edit → `apply` (or `update`) round-trips through the identifier automatically.
64
66
@@ -160,6 +162,7 @@ Run `dash0 skill show <topic>` for the reference content below, or read `referen
160
162
|`notification-channels`| Notification channel CRUD (organization-level, no dataset) |
161
163
|`otlp`| Local OTLP forwarding proxy (`otlp proxy`) |
Copy file name to clipboardExpand all lines: internal/skill/content/references/apply.md
+7Lines changed: 7 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -40,6 +40,13 @@ Exported definitions never carry the field (`get`/`list` omit it from `-o yaml`/
40
40
The CLI warns when an applied document carries a non-empty `spec.routing.assets`.
41
41
To bind a check rule, set the `dash0.com/notification-channel-ids` annotation on the check rule; to bind a synthetic check, set `spec.notifications.channels` on the synthetic check.
42
42
43
+
`SLO` documents use the same upsert-key selection as `Dash0Team`, and for SLOs `dash0.com/origin` is the recommended key rather than merely the preferred one.
44
+
SLO IDs are assigned by the server (`slo_<ulid>`), so `dash0.com/id` cannot be chosen up front — only `dash0.com/origin` can.
45
+
`dash0.com/origin` wins when present and PUTs unconditionally (create-or-replace at that origin).
46
+
When only `dash0.com/id` is present the CLI preflights the SLO with a GET — on hit it PUTs (idempotent update, the path that makes a UI-downloaded YAML reapply cleanly), on 404 it falls back to POST so a YAML from one organization applies to another as a fresh create, and other preflight errors surface instead of silently creating a duplicate.
47
+
On that POST fallback the foreign `dash0.com/id` is removed from the request body, since the server assigns the ID.
48
+
A document with neither label creates a new SLO on every apply.
49
+
43
50
`Dash0Team` documents are dispatched to the organization-level teams endpoint (also not associated with a dataset).
44
51
Upsert key selection: `dash0.com/origin` wins when present and PUTs unconditionally.
45
52
When only `dash0.com/id` is present the CLI preflights the team with a GET — on hit it PUTs (idempotent update, the path that makes a UI-downloaded YAML reapply cleanly), on 404 it falls back to POST so a YAML from one organization applies to another as a fresh create, and other preflight errors surface instead of silently creating a duplicate.
0 commit comments