Datalumina

The platform

The architecture

How an event-driven automation backend fits together, from Cloudflare and Caddy at the edge to Celery workers, the event ledger, and the managed services behind them.

A backend like this is one idea repeated. Something happens, the system writes it down, and a worker picks the work up from there. Everything else on this page exists so that keeps working when the network is slow, an API is down, or nobody is awake.

Event driven

In a direct system, a webhook handler calls Stripe, then Airtable, then Slack, and returns when all of it is done. If step three fails, the first two already happened and nothing remembers that.

In an event-driven system, the handler does one thing. It records the event and puts a job on a queue. That record is the ledger, and it holds the raw payload, a status, the number of attempts, the error if there was one, and the result. Everything else on this page depends on it. You can retry a failed run, replay an old one, catch duplicates, and answer the question "did that actually run last Tuesday" without reading logs.

events

  • iduuidOne row per event
  • sourcetextstripe, calendly, beat
  • typetextpayment.succeeded
  • payloadjsonbThe raw body, unchanged
  • statustextpending, running, done, failed
  • attemptsintHow many tries so far
  • errortextWhy the last try failed
  • resultjsonbWhat the workflow returned

Going deeper

Martin Fowler's What do you mean by event driven? separates the four patterns that hide behind the term, including event notification and event sourcing. This platform uses the first, with a durable record of each event.

What it has to guarantee

Four requirements shape every choice in the diagram below.

  • Work runs when nobody is there. A webhook or a schedule starts the task, not a person.
  • State is shared and durable. One database for the company, not local state on one laptop.
  • Failure gets caught. Retries, alerts, and recovery, because nobody is watching.
  • Access is controlled. One place for keys, integrations, and permissions.

The whole system

Read it left to right. Cloudflare handles DNS and TLS, and the Hetzner firewall drops everything that is not web traffic. Caddy terminates HTTPS inside the box and routes to FastAPI. FastAPI validates the request, writes the event, queues the job, and returns. Redis holds the queue, Celery workers do the work, and Celery Beat feeds the same queue on a schedule.

Those are the names in one working stack. Cloudflare could be any CDN, Hetzner any VPS, Supabase any managed Postgres, and Celery any queue with a scheduler attached. The order of the boxes is the part that does not change.

Separation of concerns

The diagram has three zones, and the boundaries between them matter.

The edge is deliberately not your code. Cloudflare and Caddy handle certificates, DNS, and hostile traffic, so no workflow ever contains code about any of that.

The VPS is one Docker Compose stack on a private network. Caddy, FastAPI, Redis, the workers, Celery Beat, and the monitoring stack all run there, and they reach each other by service name instead of over the internet. Running the same compose file locally is what makes the system debuggable.

The managed services are the parts you should not host yourself. Supabase holds Postgres and the ledger. The external APIs are the systems the automations act on.

Inside the VPS there is a fourth boundary, and it is the most important one. The API accepts work, the workers perform it, and those two never share a request. That is what lets a fifteen-second CRM call, a large sync, or a model call happen without the sending system ever waiting.

Firewall and IP rules

Two firewalls keep the zones apart, and both default to deny.

The Hetzner cloud firewall sits in front of the server, outside the operating system, so a rule applies even if something inside the box is misconfigured. Ports 80 and 443 are open to the world because webhooks have to arrive from anywhere. SSH is restricted to known IP addresses, and everything else is closed. Redis and the internal services are never published to the host at all, so there is no port to attack.

Supabase gets the opposite treatment. Nothing about the database needs to be public, so network restrictions limit Postgres to the server's IP address. If the credentials leak, they are useless from anywhere else, which turns a stolen connection string from an incident into a rotation task.

Order of operations

Add the database IP allow list after the server has a static IP, and keep your own address on the list while you develop. Locking Postgres down first is the fastest way to lock yourself out of your own migrations.

The vendors here are interchangeable. The rule is not. Deny by default, open only the ports that have a reason to be open, and keep the database reachable from one address. Both halves depend on the server keeping that address, which is why a fixed VPS is easier to secure than a platform that moves your workload around.

Checkpoint

You should be able to sketch the flow from a webhook to a finished workflow, and explain:

  • What the event ledger stores and why a retry is possible because of it
  • Why the API returns before the work is done
  • Which zone owns TLS, which owns execution, and which owns durable state

On this page