Arthur Gonzaga
← Back to blog

Synapse: Building a Core Messaging Service for My Homelab

After a long time, I finally dove into the world of self-hosting and Homelab. The possibilities are fascinating: setting up a custom NAS, hosting a personal media server, or building dedicated agents to automate daily routines.

However, I wanted my first major project to solve an immediate, practical problem: my 3D printer (a Creality Ender 3 V3 SE) lacks native remote monitoring capabilities.

I asked myself: Why not build a home server service to handle this?

To tackle this, I broke the project down into three distinct modules:

  1. A service to control and monitor the 3D printer from my homelab.
  2. A centralized routing service to handle notifications from the printer (and other homelab systems).
  3. A delivery service to push notifications directly to Telegram or WhatsApp.

Synapse interface

This led to the creation of Synapse: a lightweight event router that acts as the messaging engine for my entire homelab.


Design & Architecture

The core architectural pattern behind Synapse is a hub-and-spoke model.

Instead of forcing every homelab tool (OctoPrint, Uptime Kuma, custom scripts) to manage its own integration logic for messaging providers, Synapse acts as a single central hub. Specialized microservices and external webhooks act as spokes, sending standardized payloads to Synapse. Synapse then processes, filters, formats, and routes those events to the appropriate destination channel.

Synapse design

The MVP: Keeping It Simple (v0.1.0)

When I started on March 22nd, the first release (v0.1.0) was strictly minimal. It featured a single input source (an OctoPrint webhook) and a single output channel (WhatsApp via Evolution API).

At this initial stage, there was no routing engine, templating, or filtering. The payload went straight from the webhook handler to the message sender:

Goal: Send me a WhatsApp message the moment a 3D print finishes.

The technical stack was kept deliberate and lean:

  • FastAPI + Uvicorn: For high-performance async endpoint handling.
  • httpx: For outbound HTTP requests.
  • pydantic-settings: For clean configuration management.
  • uv: For fast, reliable Python package management.

The Evolution API & WhatsApp Bottleneck

While functional, relying on WhatsApp through the Evolution API quickly revealed friction points. The connection dropped periodically, and background notifications were hit-or-miss.

To improve reliability, I spent around R$ 20 to buy a dedicated SIM card and virtual phone number. I logged this number into Evolution API to broadcast print updates to a private WhatsApp group containing just myself.

While this stabilized the workflow temporarily, it was clear that WhatsApp wasn't the ideal long-term channel for infrastructure alerts.


Project Evolution: From Script to System

Over the following weeks, Synapse evolved from a basic notification script into a fully engineered platform.

Engineering Rigor & Multi-Source Ingestion (v0.2.0)

I restructured the codebase to map phone numbers dynamically per service rather than using a single global target. I also introduced Test-Driven Development (TDD) using pytest and coverage— writing failing tests prior to implementation—and configured GitHub Actions CI/CD pipelines to publish automated container builds to GHCR.

During this sprint, I opened Synapse to broader homelab events:

  • Docker daemon events: Notifying on container lifecycle changes.
  • Uptime Kuma: Alerting on service up/down states.

The Core Engine (RoutingEngine)

With multiple event sources flowing in, handling logic inline was no longer scalable. I introduced the RoutingEngine, driven by a central routes.yml configuration file. This added conditional filtering, custom message templates, and decoupled the event producer from the message consumer.

# Sample route architecture concept
routes:
  - source: octoprint
    event: PrintDone
    channel: telegram
    template: "✅ Print completed successfully: {{ payload.gcode_name }}"

Expanding the Ecosystem (MCP, Argus, & Sentinel)

As Synapse matured, specialized auxiliary services were added to the stack:

  • MCP Integration: Built a Model Context Protocol interface (moving from SSE to STDIO) exposing a generic POST /message endpoint to let AI agents trigger system notifications.
  • Argus: A specialized camera service designed to capture webcam snapshots from the 3D printer. To preserve resources, Argus only triggers snapshots while an active print job is running, exposing a /webcam/snapshot route to the Core.
  • Sentinel: A Docker watchdog utility that scans for unhealthy containers or dangling images and pushes alerts to Synapse.

Moving to Telegram

The maintenance overhead of keeping the WhatsApp delivery engine alive had become unsustainable. Maintaining an unofficial WhatsApp integration via Evolution API meant dealing with frequent session disconnects, silent notification failures on mobile, and the friction of managing a dedicated phone number just to receive system alerts. For an infrastructure monitoring tool, unreliability defeats the entire purpose.

So, I marked a fundamental shift in the architecture: deprecating WhatsApp and migrating to Telegram as the primary delivery platform.

The reliability gains were immediate:

  • Zero Session Maintenance: Unlike WhatsApp's socket connection that randomly dropped, Telegram’s official Bot API uses simple HTTP webhooks and long-polling. It just stays alive.
  • Native Media Support: Telegram natively handles image payloads seamlessly, making it trivial to send real-time webcam snapshots from Argus when a 3D print starts or finishes.
  • Instant & Guaranteed Delivery: Messages deliver reliably without hitting background sync limits or power-saving restrictions on mobile devices.
  • Replacing a brittle workaround with a robust, native API instantly turned Synapse from a service I had to constantly babysit into a quiet, dependable infrastructure backbone.

With Telegram handling delivery seamlessly, the WhatsApp module was deprecated, stabilizing the core stack once and for all.

Nowadays: From Quick Hack to Core Infrastructure

What started as a simple "let me know when the print finishes" script is now the central router for my entire homelab. Whenever a service drops, a print completes, or a camera takes a snapshot, Synapse delivers the alert instantly to my Telegram. Thanks to a central config file, I can filter out the noise and change how alerts behave on the fly—like notifying me only when a print fails—without touching any code or staring at dashboards.

By moving from WhatsApp to Telegram, writing unit tests, and running it 24/7 on my home server, Synapse stopped being a brittle workaround. It grew into a quiet, reliable piece of infrastructure I can finally trust with my eyes closed.