Repository navigation
push_outputs: is a new sink a channel, a template, or a library? #3173
Replies: 5 comments 3 replies
|
Answering my own question: I would pick C, the library. Four hand-written channels, plus the ones that keep arriving on the sidelines and stalling (#3167, and #3097 which closed as not planned), is the argument for it rather than against. Adopting a library lets us delete most of the per-channel code instead of adding a fifth copy of it, and turns the next sink into configuration rather than a review cycle. I would rather simplify what is already here and bring the sidelined work back through that path than keep growing the current shape. The one thing whoever picks this up must carry is Leaving this open for that work. |
|
Hi @IsmaelMartinez, I’m happy to pick this up. I’ll look further into Apprise and the alternatives, particularly how to preserve the existing security checks and compatibility. I’ll share my findings and proposed approach here before moving ahead with implementation. |
|
Hi @IsmaelMartinez, I’ve investigated the current implementation and tested Apprise 1.13.1 with intercepted HTTP transport. My preference for the current scope is a small native-adapter design using strategy and factory patterns. Each destination owns its payload and provider-specific behavior, while dispatch, failure isolation, and shared HTTP policy stay separate. This keeps the integrations explicit and avoids adding a runtime dependency before we have a concrete need for broader provider coverage. I recognize that this doesn’t eliminate the maintenance cost you raised: we would still own each provider’s API changes. I think that tradeoff is reasonable for a small, deliberately supported set of destinations. I opened [Draft PR #3854](#3854) to make the proposed structure concrete. It extracts the four existing sinks behind an Apprise remains a credible alternative, and its benefits go beyond making providers easy to add: it maintains provider-specific authentication, payload formatting, message splitting, and some retry/throttling mechanisms. Used as an embedded library, it also requires no additional running service. Credential storage, provisioning, rotation, and access control would still remain our responsibility. The main integration considerations I found are:
If Apprise is still your preferred direction, I’m comfortable taking that route. We would need to define the supported destination scope, enforce the equivalent of our current boundary checks, control logging, and preserve compatibility—especially the generic webhook JSON contract. I’d also clarify whether the intended scope is an additional Apprise channel or a compatible migration of existing integrations. Separately, I suggest a focused follow-up ticket for bounded retries/backoff, rate limits, idempotency and duplicate delivery, and whether non-blocking dispatch or a durable queue is warranted. These decisions apply to either approach. Failure isolation and response-status handling were already addressed in #3306/#3307, and async delivery has prior work in #3741, which I saw you parked pending this decision. I didn’t find a dedicated ticket covering the remaining delivery-policy questions. |
|
Hi @noooooooookro, now that native adapters are the agreed direction, are you interested in adapting #3167 to the |
|
both has been merged so closing this. Ideally we should add email, msteams, mqtt, sns and a few others, but we can do that once we got community interest. I would say that if we start needing to add functionality around the solution that is around deduplication, retries and we start to see a scope creep then we move to apprise (the library version). Thanks again! |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
A small decision, and it is holding up a contributor's pull request rather than a roadmap item.
push_outputshas four channels today:stdout,file,webhookandslack.webhookis generic, a JSON POST to a URL you configure.slackis the only vendor-shaped one, in that it knows the payload that service wants.Telegram has now been offered twice as a fifth. I asked #3098 to drop it so we could settle this first, and #3167 has since implemented it independently and cleanly, so the question is due. Three ways to answer it.
A, keep adding vendor channels. About thirty lines and two settings keys each, off by default. Cheap on its own. The cost is that each one is permanent, we own its payload when the vendor changes it, and Discord, Teams and Mattermost all have the same claim the moment we say yes once.
B, a body template over
webhook. Telegram becomes configuration rather than code. The cost moves to the operator, who now writes the template, and a bad one fails at runtime instead of in review.C, hand it to a library. Apprise exists for exactly this. One
apprisechannel taking a list of URLs, andtgram://bottoken/chatidis the whole Telegram feature. What I measured rather than assumed:custom_jsonfor anything else.appriseandmarkdown.requests,requests-oauthlib,click,PyYAML 6.0.1andcertifi 2024.8.30are already there and unchanged.if "apprise" in channels:block inpush_outputs, the same shape as the existing four, plus two config keys. It delivered to a local sink with the title and markdown body intact on the first run. Theimport apprisesits inside the function, so operators who do not use it pay nothing.async_notifyand a concurrent dispatch path, so it is better than the blockingrequests.postwe use now.push_outputsis already host-only with an empty allowlist for the SSRF and exfiltration reasons inconfig_security.py:14-17, so they are already in the right place.C is unlike B in that the operator does not write a payload; the library owns each service's format. It is unlike A in that we never write another vendor channel.
On whether it is too bulky, since that was the first question asked of it. 1.7MB wheel, 5.3MB unpacked, of which 3.5MB is the 150 plugins and 744K is desktop-notification assets we would never touch. Dependencies are genuinely modular: the base install needs nothing per service, because Telegram, Slack, Discord, Teams and Mattermost are all plain HTTP through
requests, and only the exotic ones (MQTT, XMPP, Growl, PGP, SMPP) sit behind anall-pluginsextra we would not install. The code is not modular: all 150 ship regardless. Importing a single plugin directly costs 130ms instead of the full scan, but then we need a name-to-class map and we are writing vendor code again, which defeats the point.The honest costs of C. It is a new runtime dependency for a feature, and AGENTS.md says to ask before adding one, which is part of why I am asking. We inherit its cadence and its security surface, and we would be shipping 150 plugins to use one. The first send in a process triggers a one-time plugin scan of about 1.2 seconds, invisible in a webhook service but real in a short CLI run. It makes
slackandwebhookredundant, so we either deprecate them and break existing config or carry both. And operators parsing the currentwebhookJSON would lose that exact shape.I lean C, with
slackandwebhookkept as they are and no deprecation, so nothing existing breaks. Second choice A for Telegram alone, since #3167 already works.@naorpeled @DanaFineTLV @ofir-frd, and @dwin-gharibi @noooooooookro since you both wrote this.
Unlike #3147 this is not necessary post-v1 work. It is one channel, not a runtime, so a quick answer unblocks someone.
All reactions