Summary
A Pushover integration created from the dashboard can never deliver.
Three layers each use their own names for the same two settings:
| Layer |
File |
User key |
API token |
| Dashboard form (writes) |
web/dash0/src/components/integrations/integration-form.tsx (ch-pushover-user / ch-pushover-token) |
user |
token |
| Sender (reads) |
server/internal/notifications/pushover.go (pushoverSettings) |
userKey |
apiToken |
| Secret encryption list |
server/internal/crypto/credentials/conn_secrets.go |
user_key |
api_token |
Two consequences:
- Delivery always fails.
parseSettings finds apiToken empty and
returns ErrPushoverAPITokenNotConfigured before any network call. That
affects every "Send test" and every real incident notification routed to
that integration.
- The credentials are stored in plaintext. Neither
user nor token is
in the encryption list, so they land in integrations.settings instead of
settings_private.
Seen in production
A new user created a Pushover integration and pressed Send test about 25
times over 10 minutes. PostHog recorded 3 $rageclick on that button. In
between they re-entered the token, toggled Enabled and Default
off and on, deleted the integration and recreated it twice. All three rows in
prod have settings keys user,token, with an empty settings_private_keys.
The one test request still in the logs completed in 6.7 ms, which is too
fast to have reached api.pushover.net, so it failed on the missing-key path.
The user then left (and later deleted their organization).
Every Pushover integration in prod has this shape, so the type has never
worked from the dashboard.
Proposed fix
- Pick one canonical pair. The encryption list's
user_key / api_token
matches the snake_case of other secret settings (api_key, app_token,
routing_key). Use it in the form, the sender and the docs
(web/docs/docs/configuration/notifications.md § Pushover).
- Make the sender tolerant during the transition. Read the canonical keys and
fall back to user/token and userKey/apiToken, so existing rows work
before the migration runs.
- Add a migration that renames the keys on existing
pushover rows and moves
the secrets into settings_private (encrypted), so nothing stays in
plaintext.
- Check the user-level Pushover contact (
models/user_contact.go) and the
escalation-step sender (jobtypes/job_escalation_step.go) for the same
drift.
- Add a guard test: for every integration type, the keys the dashboard form
writes must cover what the sender requires, and every secret field the form
writes must be in conn_secrets.go. This class of drift should fail CI, not
a customer.
Separate UX gap seen in the same session
The test result badge tells the user it failed, but nothing pointed at why
in a way they could act on: they kept retyping a token that was correct.
Once (1) is fixed this goes away for Pushover. Still, a "not configured" error
from the sender should be shown as a configuration bug ("this integration is
missing its API token"), not as a delivery failure.
Acceptance
- Create Pushover from the dashboard, then Send test: a push arrives, and
the badge says delivered.
- The settings row has no
user/token in clear; settings_private_keys
lists both.
- Existing rows are migrated, and the CI guard test fails if a form and its
sender drift again.
Summary
A Pushover integration created from the dashboard can never deliver.
Three layers each use their own names for the same two settings:
web/dash0/src/components/integrations/integration-form.tsx(ch-pushover-user/ch-pushover-token)usertokenserver/internal/notifications/pushover.go(pushoverSettings)userKeyapiTokenserver/internal/crypto/credentials/conn_secrets.gouser_keyapi_tokenTwo consequences:
parseSettingsfindsapiTokenempty andreturns
ErrPushoverAPITokenNotConfiguredbefore any network call. Thataffects every "Send test" and every real incident notification routed to
that integration.
usernortokenisin the encryption list, so they land in
integrations.settingsinstead ofsettings_private.Seen in production
A new user created a Pushover integration and pressed Send test about 25
times over 10 minutes. PostHog recorded 3
$rageclickon that button. Inbetween they re-entered the token, toggled Enabled and Default
off and on, deleted the integration and recreated it twice. All three rows in
prod have settings keys
user,token, with an emptysettings_private_keys.The one test request still in the logs completed in 6.7 ms, which is too
fast to have reached
api.pushover.net, so it failed on the missing-key path.The user then left (and later deleted their organization).
Every Pushover integration in prod has this shape, so the type has never
worked from the dashboard.
Proposed fix
user_key/api_tokenmatches the snake_case of other secret settings (
api_key,app_token,routing_key). Use it in the form, the sender and the docs(
web/docs/docs/configuration/notifications.md§ Pushover).fall back to
user/tokenanduserKey/apiToken, so existing rows workbefore the migration runs.
pushoverrows and movesthe secrets into
settings_private(encrypted), so nothing stays inplaintext.
models/user_contact.go) and theescalation-step sender (
jobtypes/job_escalation_step.go) for the samedrift.
writes must cover what the sender requires, and every secret field the form
writes must be in
conn_secrets.go. This class of drift should fail CI, nota customer.
Separate UX gap seen in the same session
The test result badge tells the user it failed, but nothing pointed at why
in a way they could act on: they kept retyping a token that was correct.
Once (1) is fixed this goes away for Pushover. Still, a "not configured" error
from the sender should be shown as a configuration bug ("this integration is
missing its API token"), not as a delivery failure.
Acceptance
the badge says delivered.
user/tokenin clear;settings_private_keyslists both.
sender drift again.