agent-pages

Agent Control Plane and RSS Delivery — Zulip Handoff Canvas

Download

Agent Control Plane and RSS Delivery — Zulip Handoff Canvas

Purpose: Give an implementation agent enough context to continue the project without rereading the original conversation.

Status: Product direction chosen; implementation not yet started.

Decision date: 8 August 2026


1. Executive summary

The project began as a search for a better control plane for personal and autonomous AI agents such as OpenClaw and Hermes Agent, beyond the usual Telegram, Discord, and Slack interfaces. The required experience is conversation-native rather than dashboard-heavy: durable threaded conversations, granular notifications, predictable unread behavior, strong iOS support, and the ability to distinguish messages that have actually been read from messages merely opened in a channel.

The final platform choice is self-hosted Zulip. Its decisive advantage is the first-class channel + topic model. Topics are not optional side threads; they are the normal unit of conversation. This fits agent sessions, approval requests, background jobs, alerts, and RSS sources much better than a flat channel containing interleaved messages. Zulip also offers per-channel and followed-topic notification controls, topic muting/following, useful unread navigation, a capable API, bot accounts, and configurable message retention for a resource-constrained deployment.

Mattermost was the other serious contender. It is closer to Slack, has familiar channels and reply threads, and may have a shorter path for integrations that already target Slack/Mattermost-style systems. It was rejected for this project because its conversation and unread model is less aligned with the desired workflow: reply threads are secondary to the channel timeline, and it does not provide the same topic-centric inbox and catch-up experience as Zulip.

RSS delivery is also part of the control plane. Existing feeds currently publish through MonitoRSS into Discord. The target design is Miniflux → lightweight relay → Zulip API, with RSS categories mapped to Zulip channels and publications mapped to topics. Messages should use clean Zulip Markdown and native website previews. The visual goal is roughly 80–90% of a Telegram link preview and 55–65% of a rich Discord embed; exact Discord cards are not possible without modifying Zulip clients.


2. User goal and operating constraints

Primary goal

Create a self-hosted, mobile-friendly conversational control plane where humans can:

Important constraints


3. Interaction model being sought

The desired system behaves more like an asynchronous work inbox than a group chat:

  1. An agent starts or updates a bounded task.
  2. Every task has a stable conversation location.
  3. Routine progress is visible but quiet.
  4. Approval requests, failures, and direct mentions can notify immediately.
  5. Reading messages progressively reduces the unread state; opening a busy channel should not silently mark everything read.
  6. RSS and background automation remain browseable without polluting important agent work.

Example conceptual organization:

Channel: agents — openclaw
  Topic: deploy staging build
  Topic: weekly dependency audit
  Topic: investigate payment failure

Channel: agents — hermes
  Topic: research agent control planes
  Topic: summarize customer calls

Channel: approvals
  Topic: production database migration

Channel: RSS — AI
  Topic: MIT News
  Topic: Hacker News
  Topic: The Pragmatic Engineer

4. Platforms considered

The initial problem was broader than choosing between Zulip and Mattermost. Telegram, Discord, and Slack were the familiar baselines, but each had friction for an agent control plane.

Platform Useful qualities Main mismatch for this project
Telegram Excellent mobile app, efficient push delivery, simple bots, link previews Conversation organization is comparatively flat; topics depend on group/forum setup; notification and work-inbox controls are weaker
Discord Strong bot ecosystem, webhooks, rich embeds, threads, attractive automation messages Designed around community chat; threads and unread semantics are not ideal for disciplined asynchronous task handling
Slack Mature integrations, familiar workplace model, threads, granular notifications SaaS dependence/cost and channel-first conversation model; self-hosting is not available
Mattermost Self-hosted Slack-like experience, integrations, familiar mobile app Threads remain attached to a flat channel timeline; weaker topic-first catch-up model
Zulip Self-hosted, first-class topics, topic following/muting, structured unread workflow, capable API Fewer ready-made agent integrations and less visual customization than Discord embeds

Other possible products may exist, but no additional contender identified in the conversation displaced Zulip on the combined requirements of self-hosting, topic-native conversation, unread handling, and notifications.


5. Zulip versus Mattermost decision

Why Zulip won

Topics are native, not an attachment

Every Zulip channel message belongs to a topic. This provides a durable address for an agent job, source, incident, or approval. Users can follow, mute, search, and navigate conversations by topic. Mattermost supports threaded replies, but the main channel remains a chronological stream, so unrelated top-level posts are easier to interleave.

Better high-volume catch-up model

Zulip exposes unread conversations and supports navigation to the next unread topic, next unread followed topic, and next unread direct-message conversation. Reading strategies are built around moving through individual conversations. Jumping to the bottom explicitly marks the current view read; merely having a channel open is not intended to erase the entire backlog.

The earlier comparison with Telegram/Discord/Mattermost focused on whether unread counts change while scrolling. The practical conclusion was:

Exact behavior can vary by client version and view. The implementation agent should run an iOS and desktop acceptance test before migrating fully.

Notification control matches agent work

Zulip can distinguish:

This allows a useful policy: mute noisy agent and RSS channels by default, follow active task topics, and reserve push notifications for followed topics, mentions, approvals, and failures.

What is lost by not choosing Mattermost

These losses were accepted because the topic and unread model is more important than connector convenience.

iOS app and battery conclusion

Zulip appeared stronger for notification granularity because of followed topics and per-channel policies. Mattermost remains a mature iOS application, but its notification hierarchy is more channel/mention oriented.

No reliable controlled benchmark of idle battery drain was found in the conversation. Both should normally rely on Apple push notifications rather than continuous foreground polling. Actual drain will depend on app version, server/push configuration, notification volume, and whether background refresh is enabled. The handoff agent should not claim a battery winner without measuring both apps on the target iPhone over comparable 24-hour periods.


6. Agent integration: OpenClaw and Hermes Agent

Current working conclusion

Neither OpenClaw nor Hermes Agent should be assumed to include a first-party Zulip connector. Their exact connector/plugin status can change and must be verified against the versions that will be deployed.

Do their core source trees need to be modified?

Probably not. The preferred implementation is an external adapter or plugin using public extension points:

Zulip events/API
      ↕
Zulip adapter/plugin
      ↕
OpenClaw or Hermes public message/session interface

Possible adapter behavior:

Only modify OpenClaw/Hermes core code if the selected release exposes no stable plugin, webhook, command, or transport interface. Even then, prefer a small upstreamable connector module over a private fork.

Required verification before implementation


7. Self-hosting and retention

Self-hosting

Zulip supports self-hosting. Production deployment requires normal operational care: HTTPS, email delivery, backups, upgrades, monitoring, and mobile push configuration. Self-hosted mobile notifications must be explicitly verified after setup.

Message expiry requirement

The original question was whether Zulip supports Telegram-style disappearing messages such as “delete everything older than seven days.” The actual need is server storage control, not secrecy or ephemeral chat.

Use server-side message retention for noisy automation/RSS channels if the deployed Zulip edition/version supports the necessary scope. Do not model this as client-side disappearing messages. The implementation agent must confirm whether retention can be configured globally, per organization, or per channel in the chosen release and plan.

Suggested policy:

Data Suggested retention
RSS channels 7–30 days
Routine agent progress/logs 30–90 days
Approval and audit conversations Longer, based on risk/compliance needs
Uploaded RSS images Avoid uploading where possible
Backups Separate lifecycle policy; retention deletion does not automatically shrink every backup

Storage planning must include PostgreSQL, search indexes, uploaded files, preview/cache data, logs, and backups—not merely visible message text. Database deletion may not immediately return filesystem space without normal PostgreSQL maintenance.


8. Message customization: what Zulip can and cannot do

The desired examples were a rich Discord RSS embed and a Telegram link-preview card. A realistic Zulip approximation was generated during the conversation and judged sufficient.

Supported presentation

Not supported as native message controls

Zulip messages use Zulip-flavored Markdown and server-controlled rendering. Website previews depend on Open Graph/site metadata and server settings. Poor metadata, anti-bot protections, or inaccessible images can produce incomplete previews.

Bot identity strategy

Zulip bot identity is tied to its API credentials. To reproduce publisher branding exactly, create separate bots such as MIT News and The Pragmatic Engineer, each with its own avatar. This becomes cumbersome across many feeds.

Recommended compromise:


9. RSS replacement for MonitoRSS

Options considered

Option Advantages Disadvantages Decision
Zulip's official rss-bot Very low overhead; no additional web UI/database; cron-based Basic formatting and management; weaker for bulk feeds and custom routing Good fallback/minimal deployment
Miniflux + relay Web UI, feed health, categories, deduplication, OPML, webhooks, flexible Zulip formatting Requires Miniflux, PostgreSQL, and a small relay Recommended
n8n Visual workflows, transformations, summaries, complex routing Heavier in memory/operations; excessive for simple RSS forwarding Optional future enhancement
Custom polling bridge Lowest possible overhead and complete control Must build feed management, polling, retries, deduplication, and UI/config Use only if Miniflux is too heavy

Chosen architecture

Existing RSS feeds
       ↓
    Miniflux
  polling, health,
  categories, dedupe
       ↓ webhook
RSS-to-Zulip relay
 clean HTML, select image,
 format Markdown, route,
 retry and dedupe
       ↓
   Zulip REST API
 channel + topic + bot

Miniflux should use its own PostgreSQL database. It may share the same PostgreSQL server with Zulip, but its tables should not be mixed into Zulip's database.

Relay responsibilities

  1. Validate the Miniflux webhook signature.
  2. Accept new_entries events.
  3. Extract feed name, category, article title, URL, author, publication date, summary/content, tags, and media/enclosures.
  4. Sanitize feed HTML and convert a safe subset into Zulip Markdown.
  5. Select a useful image from media/enclosure fields or page metadata.
  6. Truncate excerpts to a configurable limit, initially around 300–600 characters.
  7. Resolve category/feed rules into a Zulip channel, topic, and bot credential.
  8. Post through the Zulip send-message API.
  9. Store a stable article identity/hash to avoid duplicates during retries.
  10. Log failures without retaining entire article bodies indefinitely.
  11. Retry transient Zulip/network failures with bounded exponential backoff.
  12. Offer a dry-run/preview mode before bulk migration.

Important caveat: Miniflux documentation states that its webhook delivery may not retry failed events. Therefore, the relay endpoint must acknowledge only after safely accepting/persisting the event, or a reconciliation job should periodically compare recent Miniflux entries with delivered IDs.


10. RSS channel/topic structure

Recommended default: topic per publication

Channel: RSS — Technology
  Topic: MIT News
  Topic: The Pragmatic Engineer
  Topic: Hacker News

This is best for passive consumption. Users can follow or mute an entire publication, unread counts remain understandable, and the number of topics stays bounded by the number of feeds.

Alternative: topic per article

Channel: RSS — Technology
  Topic: Alexander Rakhlin named director…
  Topic: Why is Meta destroying its engineering organization?

This is better when agents or humans will discuss each article in depth. It creates many short-lived topics and makes publication-level muting less convenient.

Chosen initial structure

Use topic per publication. If a particular article needs analysis, start a dedicated topic in a research/agent channel and link back to the RSS message. Reconsider topic-per-article only for selected high-value feeds.

Suggested mappings:

Miniflux category Zulip channel Topic
Artificial Intelligence RSS — AI Feed name
Technology RSS — Technology Feed name
Security RSS — Security Feed name
Business RSS — Business Feed name
Uncategorized RSS — General Feed name

Example target message

## [Alexander Rakhlin named director of the MIT Statistics and Data Science Center](ARTICLE_URL)

*Institute for Data, Systems, and Society*

Alexander “Sasha” Rakhlin PhD '06 has been named the next director
of the MIT Statistics and Data Science Center…

> “Sasha is one of the sharpest theoretical minds working in
> statistics and machine learning today…”

[Read the full article →](ARTICLE_URL)

`MIT News · Artificial Intelligence · 8 April 2026`

Sending the article URL allows Zulip to attempt a native website preview. The exact card layout remains controlled by Zulip and the source site's metadata.


11. Proposed relay configuration

defaults:
  bot: rss
  channel: "RSS — General"
  topic: feed_name
  excerpt_characters: 500
  include_author: true
  include_date: true
  include_image: auto
  website_preview: true

categories:
  Artificial Intelligence:
    channel: "RSS — AI"
  Technology:
    channel: "RSS — Technology"
  Security:
    channel: "RSS — Security"
  Business:
    channel: "RSS — Business"

feeds:
  MIT News - Artificial Intelligence:
    bot: mit-news
    topic: "MIT News"
  The Pragmatic Engineer:
    bot: pragmatic-engineer
    topic: "The Pragmatic Engineer"

retention:
  delivered_ids_days: 90
  failure_logs_days: 30

Secrets such as Zulip bot keys, Miniflux credentials, and webhook secrets must be injected through environment variables or a secret manager, not committed in this configuration.


12. MonitoRSS migration plan

There was no clearly documented one-click MonitoRSS-to-Zulip or MonitoRSS-to-OPML importer identified. Migration is nevertheless practical because the essential data is the list of feed URLs plus optional names/categories/formatting rules.

Source extraction

Transformation

Transform the extracted data into:

  1. An OPML file for Miniflux subscriptions.
  2. A mapping report containing old Discord destination, feed name, URL, category, filters, and custom formatting.
  3. Relay configuration mapping categories/feeds to Zulip channels, topics, and optional branded bots.

Suggested intermediate CSV columns:

feed_name,feed_url,category,old_discord_channel,old_template,
zulip_channel,zulip_topic,zulip_bot,enabled,notes

Safe cutover

  1. Export and back up MonitoRSS configuration.
  2. Normalize and deduplicate feed URLs.
  3. Import a small test OPML into Miniflux.
  4. Run the relay in dry-run mode and inspect formatted messages.
  5. Enable delivery into a private Zulip test channel.
  6. Validate images, excerpts, Unicode, long titles, dates, duplicate handling, and broken feeds.
  7. Import the remaining feeds in batches.
  8. Run MonitoRSS and Zulip in parallel briefly, comparing delivery counts.
  9. Disable MonitoRSS only after parity is acceptable.
  10. Retain the export and migration report for rollback.

Historical Discord messages do not need to be migrated unless explicitly requested. The initial goal is subscription/configuration continuity, not replaying the entire old feed history.


13. VPS storage and cleanup design

Principles

Data lifecycle

Component Data retained Cleanup mechanism
Miniflux Feeds, entries, status, metadata Periodic removal of old read entries according to supported settings/jobs
Relay Delivered IDs/hashes, retry state, minimal failure details TTL purge, e.g. 90 days for delivered IDs
Zulip Rendered RSS and agent messages Server retention policy, ideally shorter for RSS channels
Reverse proxy/system services Access/error logs logrotate or equivalent
Backups Database/config snapshots Separate daily/weekly retention policy

Do not estimate disk consumption solely from RSS text. Images uploaded to Zulip, full-text feed content, database indexes, search indexes, and backups are the likely growth drivers.


14. Implementation phases

Phase 0 — verify assumptions

Phase 1 — Zulip pilot

Phase 2 — basic agent adapter

Phase 3 — RSS pilot

Phase 4 — MonitoRSS migration

Phase 5 — cleanup and hardening


15. Acceptance criteria

Agent control plane

RSS

Operations


16. Risks and unresolved questions

  1. Agent connector availability: Recheck current OpenClaw/Hermes releases; prior conclusions may age quickly.
  2. Unread semantics: Perform a hands-on iOS test with a deliberately long unread topic and multiple channels.
  3. Battery drain: No evidence-backed winner was established; measure on the target device.
  4. Retention scope: Confirm exact global/per-channel controls in the deployed Zulip edition and version.
  5. Self-hosted push: Verify Zulip's supported push-notification configuration and any service/plan dependencies.
  6. MonitoRSS export: Establish an official/API/database extraction path and determine which custom rules can be translated.
  7. Webhook loss: Design around Miniflux webhook non-retry behavior through durable intake or reconciliation.
  8. Website previews: Some sources will have missing/broken Open Graph metadata or block preview fetching.
  9. Publisher-specific bots: Decide the acceptable operational limit before creating many credentials.
  10. Rate limits and bursts: Batch-heavy feeds can create bursts; relay must queue and pace Zulip API calls.
  11. Security: Agent commands require strict allowlists and explicit approval gates for destructive or privileged actions.

17. Key references

Zulip

Miniflux

MonitoRSS


18. Concise handoff directive for the next agent

Continue this project as an implementation and validation task. Preserve the decision to use self-hosted Zulip unless testing reveals a blocking failure. First verify the target Zulip retention/push behavior, current OpenClaw and Hermes extension interfaces, and the available MonitoRSS export path. Then prototype two thin components: (1) a Zulip adapter that maps channel/topic conversations to stable agent sessions without modifying agent core code, and (2) a durable Miniflux-to-Zulip relay that formats safe Markdown, routes category → channel and feed → topic, handles images/previews gracefully, deduplicates deliveries, and supports a dry-run. Optimize for a limited-disk VPS, iOS notification control, predictable unread behavior, secure approvals, and reversible migration.