Skip to content

Add Mastodon alert provider - #1372

Open
xchose wants to merge 7 commits into
fluxcd:mainfrom
xchose:mastodon-provider
Open

xchose wants to merge 7 commits into
fluxcd:mainfrom
xchose:mastodon-provider

Conversation

@xchose

@xchose xchose commented Aug 16, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #447

Adds a mastodon Provider type for posting Flux events as statuses on a Mastodon account, via a plain HTTP POST to the /api/v1/statuses endpoint. No SDK dependency; like every other notifier it only needs the shared postMessage client.

Implementation

  • The address is the server root URL (e.g. https://mastodon.social); the /api/v1/statuses path is appended automatically (and preserved if already present).
  • Auth: OAuth access token with the write:statuses scope (Secret key token), sent as a bearer token.
  • Status text: severity emoji (💫/🚨, same convention as the Telegram provider), involved object kind/name.namespace, event message, and event metadata as key-value lines. Truncated to 500 characters, the default Mastodon server limit.
  • The request payload carries only status. Visibility follows the account's default posting visibility (Preferences → Posting defaults); "Quiet public" or "Followers only" is recommended for alert accounts and documented in the spec.
  • The Idempotency-Key header carries the event key the event server already uses for rate limiting. The key computation moved from eventKeyFunc to notifier.EventKey, is computed once in eventMiddleware and passed to notifiers through the context (the notifier cannot recompute it, since the token metadata is stripped before dispatch). Mastodon keeps the key for one hour, so retried requests and identical events within that window do not create additional statuses.
  • Proxy and TLS configuration from the Provider spec are respected.

Sample rendered payload:

{
  "status": "💫 gitrepository/podinfo.flux-system\nstored artifact for commit 'master@sha1:3e0ff8a...'\n\nrevision: master@sha1:3e0ff8a..."
}

Testing

  • internal/notifier/mastodon_test.go: endpoint path, bearer auth header, Idempotency-Key taken from the context and omitted without it, payload shape without visibility, severity emoji, 500-char truncation, sorted metadata, path preservation and constructor validation against an httptest server.
  • internal/notifier/eventkey_test.go: key stability and context round-trip.
  • internal/controller/provider_controller_test.go: the mastodon type is accepted by the API server.
  • Full envtest suite passes; make tidy fmt vet generate manifests api-docs leave a clean tree.

Manual e2e testing

kind cluster, controller image and CRD from this branch, real mastodon.social account. Secret: address: https://mastodon.social, token with write:statuses. Verified against an in-cluster echo server and then against mastodon.social:

  • payload is {"status": ...} only, Idempotency-Key equals the rate-limiter digest recomputed by hand and is stable across identical events;
  • info, error and recovery events from a podinfo GitRepository posted three statuses without errors;
  • statuses were public before changing the account setting and unlisted after switching it to "Quiet public";
  • a repeated identical event past a 10s rate limit created no duplicate status.
mastodon-info mastodon-error

🤖 Generated with Claude Code

@xchose
xchose force-pushed the mastodon-provider branch from a2c1576 to f8ab539 Compare August 31, 2026 06:03
@xchose

xchose commented Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto latest main, CI has never run on this PR: could a maintainer approve the run?

@xchose

xchose commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

CI is green now — thanks for approving the run.

The branch is rebased on main and still merges cleanly. Locally make test passes with a clean tree after codegen, and I verified the provider end-to-end against a real mastodon.social account.

Could a maintainer take a look when there's time?

@matheuscscp

Copy link
Copy Markdown
Member

It feels so strange to send a Flux alert to a social network 🤔 🤔 🤔

@stefanprodan

Copy link
Copy Markdown
Member

It feels so strange to send a Flux alert to a social network

Mastodon can be used like Slack, but self-hosted and private.

Comment thread docs/spec/v1beta3/providers.md Outdated
Comment thread internal/notifier/mastodon.go Outdated
@xchose

xchose commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor Author

Re-ran the e2e on kind against a real mastodon.social account after the review changes: payload is {"status": ...} only, Idempotency-Key matches the rate-limiter digest, visibility follows the account setting, and a repeated identical event created no duplicate status.
mastodon-error
mastodon-info
mastodon-profile-scrolled
mastodon-profile

Add the mastodon Provider type for posting Flux events as statuses on
a Mastodon account. Events are published with a plain HTTP POST to the
/api/v1/statuses endpoint of the server given in the address, using an
OAuth access token with the write:statuses scope as a bearer token.

The status text carries a severity emoji, the involved object, the
event message and the event metadata as key-value lines in sorted key
order, truncated to the 500-character default server limit. An
Idempotency-Key header derived from the event guards against duplicate
statuses when the HTTP client retries a request whose response was
lost. An optional visibility query parameter on the address maps to the
status visibility field, since the Provider API has no dedicated field
for it.

Assisted-by: Claude Code/claude-fable-5
Signed-off-by: Chose Carreras <xchose@gmail.com>
Carrying the status visibility as a query parameter on the Provider
address was an ad hoc convention that could be mistaken for a parameter
accepted by the Mastodon server. Remove it and leave the visibility
field out of the payload, so statuses are posted with the default
posting privacy configured on the account. A dedicated Provider input
can be added later once there is a generic mechanism for provider
specific settings.

Signed-off-by: Martin Cholewa <xchose@gmail.com>
Assisted-by: Claude Code/claude-fable-5-1
The Mastodon provider computed its own idempotency key from the object
UID, reason, timestamp and status text, giving an event a second
identity next to the one the event server already uses for rate
limiting.

Move the key computation from the event server into notifier.EventKey
and pass the key to notifiers through the context. The notifier cannot
recompute it from the event it receives, because the token metadata is
removed and the group prefix of the revision keys is stripped before
dispatch. The Mastodon provider now sends that key and falls back to
deriving it from the event when called outside the event server.

Mastodon keeps idempotency keys for one hour, so identical events within
that window yield a single status. This is documented in the spec.

Signed-off-by: Martin Cholewa <xchose@gmail.com>
Assisted-by: Claude Code/claude-fable-5-1
The setting lives under Preferences → Posting defaults and the Mastodon
web interface names the unlisted visibility Quiet public.

Signed-off-by: Martin Cholewa <xchose@gmail.com>
Assisted-by: Claude Code/claude-fable-5-1
Compute the event key in eventMiddleware and store it in the request
context, so the rate limiter and the notifiers share one identity by
construction instead of hashing the event twice per request.

Drop the Mastodon fallback that derived the key from the event, since
the notifier only sees the alert-mutated copy and the result could not
match the rate limiter key. Without a key in the context no
Idempotency-Key header is sent.

Add the mastodon type to the provider API server validation test and
restore the alphabetical order of the provider table in the spec.

Signed-off-by: Martin Cholewa <xchose@gmail.com>
Assisted-by: Claude Code/claude-fable-5-1
@xchose

xchose commented Sep 23, 2026

Copy link
Copy Markdown
Contributor Author

Rebased on main, review comments addressed. Could a maintainer approve the CI run?

Comment thread internal/server/event_handlers.go Outdated
The event key was forwarded from the request context into the
notifier's context in dispatchNotification and read back inside
Mastodon.Post, unlike every other notifier input which is wired
through getNotificationParams and createNotifier.

Read the key computed by eventMiddleware in getNotificationParams,
before the event is mutated, and hand it to createNotifier as a
notifier option. The Mastodon notifier stores it at construction and
sends it as the Idempotency-Key header. The context helpers in the
notifier package are removed; the key stays in the request context
only within the event server, where the middleware computes it once
for the rate limiter.

Signed-off-by: Martin Cholewa <xchose@gmail.com>
Assisted-by: Claude Code/claude-fable-5-1
@xchose

xchose commented Sep 23, 2026

Copy link
Copy Markdown
Contributor Author

Review comment addressed in f7358bd, the event key is wired through createNotifier options like the other notifier inputs.

Re-ran the e2e on kind against a real mastodon.social account with this change: Idempotency-Key matches the rate-limiter digest and is stable across repeated identical events, and five dispatches (info, the same error three times, recovery) created exactly three statuses. Could a maintainer approve the CI run?

@matheuscscp matheuscscp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One last round and we should be good 🙏

Comment thread internal/server/event_handlers.go Outdated
Comment thread internal/server/event_server.go Outdated
createNotifier already receives the request context, so it can pick
up the event key stored there by eventMiddleware itself instead of
having getNotificationParams pass it down.

Drop the fallback that recomputed the key from the event: the event
metadata is mutated while the request is processed, so a recomputed
key would not match the one used by the rate limiter, and the key is
always present in the context anyway.

Signed-off-by: Martin Cholewa <xchose@gmail.com>
Assisted-by: Claude Code/claude-fable-5-1

@matheuscscp matheuscscp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@xchose

xchose commented Sep 23, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @matheuscscp for the thorough review, I learned a lot about how the event server and the notifiers fit together.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add mastodon as a notification channel

3 participants