---
title: "What the system is made of"
description: "The three services behind the CloudFly Helpdesk console, the path a message takes through them, and why losing the realtime link is not losing a message."
updated: "2026-08-28"
audience: [agent, administrator]
source: "https://helpdesk-docs.svr1.feedapp.click/en/khai-niem/kien-truc-tong-quan"
---

The console you open each morning is one of three services that run
independently of one another. Knowing how the three divide the work is enough
to read two familiar situations: a colleague's message appearing the instant
they send it, and the status line reporting a lost connection.

This page describes the shape of the system at the level a console user needs,
not at the level of whoever deploys it. Ports, environment variables and build
steps live in the `README.md` file in the source tree.

## Three services, three different jobs [#three-services-three-different-jobs]

| Service                     | What it does                                                          | What it does not do                          |
| --------------------------- | --------------------------------------------------------------------- | -------------------------------------------- |
| Console (the web interface) | Draws the queue, sends REST requests, holds one Socket.IO connection  | Never reads the database directly            |
| `api-engine`                | Answers every REST request and is the **only** writer to the database | Does not push data into a browser on its own |
| `ws-engine`                 | Replays events down to the browsers watching a given conversation     | **Never** writes to the database             |

The three share no code. They meet in exactly two places: Postgres, where
everything is stored, and Redis, the internal line between the two engines.

The chat panel on a customer's website talks to `api-engine` as well, through
its own widget door and with its own token. One body of data, two entrances,
and each entrance has its own reach.

## The path a message takes [#the-path-a-message-takes]

1. A customer or you sends a message. The request reaches `api-engine` over
   REST.
2. `api-engine` writes the message into Postgres and waits for the write.
3. Once the row is committed, it publishes a `message.created` event on the
   Redis `chat_events` channel.
4. `ws-engine`, listening on that channel, turns it into a `new_message` event
   and pushes it into the room for that conversation.
5. Every browser holding that conversation open appends the message, with no
   page reload.

That is why a message shows up almost instantly in a colleague's window: it
waits for nobody to press refresh, it travels the Redis line down to the
socket.

## Write first, publish second [#write-first-publish-second]

The order of steps 2 and 3 is a promise the system keeps, not an accident. The
realtime event is born **after** the row is already in the database.

Two consequences worth remembering. First: a message you watch land in the
thread has been stored, and a page reload still shows it. Second: the reverse
cannot happen, because `ws-engine` holds no write access, so no message ever
lives on the socket alone.

> **Note:** Losing the realtime link costs you live updates, not data. Messages are still
> written as usual and all of them are there when you reopen the conversation,
> because that read goes over REST rather than over the socket.

## What you observe when a part stops [#what-you-observe-when-a-part-stops]

| Part that stops      | What you observe                                                                                                 |
| -------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `ws-engine` or Redis | Messages still send and still store; open windows stop updating themselves and need a reload to show new traffic |
| `api-engine`         | Nothing sends and no queue opens: every REST request goes through it                                             |
| Postgres             | No service can work at all, since this is where the data lives                                                   |

The table gives you something an operations engineer can act on: "I can send
but I cannot see other people's messages" and "I cannot send" point at two
different broken areas.

## Read next [#read-next]

* [Roles and per-feature permissions](/en/khai-niem/vai-tro-va-quyen)
* [Shared inboxes and channels](/en/khai-niem/inbox-va-kenh)
* [Glossary of helpdesk terms](/en/khai-niem/thuat-ngu)
