---
sidebar_position: 10
---

# Architecture

We begin the guide with a high-level overview of the architecture of the CDR Link deployment framework.

## Introduction

We deploy each CDR Link instance on a single
[Red Had Enterprise Linux](https://www.redhat.com/en/technologies/linux-platforms/enterprise-linux)
9 (or compatible) host using
[rootless Podman](https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md).
Each component of Link is a container instance with the containers managed via systemd using
[Podman Quadlet](https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html).
Components communicate via isolated networks that are also configured via Quadlet, and make use of the
[slirp4netns](https://github.com/rootless-containers/slirp4netns) user-mode networking for unprivileged
network namespaces.

Both
[discretionary access controls](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/configuring_basic_system_settings/managing-file-system-permissions_configuring-basic-system-settings)
and
[SELinux](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html-single/using_selinux/index)
are used to prevent lateral movement between containers should a container be compromised, with particular
attention given to the messaging channels WhatsApp and Signal.
No container runs its application as the inside "root" user, which is an unprivileged user on the host.

The `/home` mount point on the host is encrypted using
[LUKS](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/managing_storage_devices/encrypting-block-devices-using-luks_managing-storage-devices)
with a per instance key to protect all user data at rest.
Further, we use separate partitions for critical audit logging to ensure that a resource exhaustion attack cannot
prevent later investigation, and automatically shut down instances where there is no space available for audit logging.

## Components

The following diagram shows the dependency relationships between the components of CDR Link.
If you use our [Ansible role](./deploy.md) for deployment then these will be automatically configured.
The Link stack containers are
[OCI](https://opencontainers.org/) compliant containers and you can run these with alternatives
such as [Docker Compose](https://docs.docker.com/compose/) however we would not be able to provide support for this
setup.
Our former Docker Compose deployment framework has been deprecated and we intend to migrate all partners to our new
rootless Podman setup for the improved reliability, performance, and security.

:::info
While the following diagram refers to `.service` units, these are the units generated by Podman Quadlet.
The service definitions are in `.container` units within the Quadlet directory at `$HOME/.config/containers/systemd`.
:::
```mermaid
flowchart TD
    bridge-worker.service --> bridge-postgresql.service
    bridge-worker.service -.-> bridge-whatsapp.service
    bridge-worker.service -.-> signal-cli-rest-api.service

    link.service --> bridge-postgresql.service
    link.service --> bridge-worker.service

    opensearch-dashboards.service --> zammad-opensearch.service

    zammad-nginx.service --> zammad-railsserver.service
    zammad-nginx.service --> link.service

    zammad-storage.target{zammad-storage.target}
    zammad-storage.target --> zammad-postgresql.service
    zammad-storage.target --> zammad-redis.service
    zammad-storage.target --> zammad-memcached.service
    zammad-storage.target --> zammad-opensearch.service

    zammad-railsserver.service -.-> zammad-init.service

    zammad-init.service --> zammad-storage.target
    zammad-railsserver.service --> zammad-storage.target
    zammad-scheduler.service --> zammad-storage.target
    zammad-websocket.service --> zammad-storage.target

    link.target{link.target}
    link.target --> opensearch-dashboards.service
    link.target --> zammad-nginx.service
    link.target --> zammad-scheduler.service
    link.target --> zammad-websocket.service
```

All of the containers are built from the same [source repository](https://gitlab.com/digiresilience/link/link-stack)
and published in a public registry:

| Container Name        | Image                                                                             |
|-----------------------|-----------------------------------------------------------------------------------|
| bridge-postgresql     | `registry.gitlab.com/digiresilience/link/link-stack/postgresql:latest`            |
| bridge-whatsapp       | `registry.gitlab.com/digiresilience/link/link-stack/bridge-whatsapp:latest`       |
| bridge-worker         | `registry.gitlab.com/digiresilience/link/link-stack/bridge-worker:latest`         |
| link                  | `registry.gitlab.com/digiresilience/link/link-stack/link:latest`                  |
| opensearch-dashboards | `registry.gitlab.com/digiresilience/link/link-stack/opensearch-dashboards:latest` |
| signal-cli-rest-api   | `registry.gitlab.com/digiresilience/link/link-stack/signal-cli-rest-api:latest`   |
| zammad-init           | `registry.gitlab.com/digiresilience/link/link-stack/zammad:latest`                |
| zammad-memcached      | `registry.gitlab.com/digiresilience/link/link-stack/memcached:latest`             |
| zammad-nginx          | `registry.gitlab.com/digiresilience/link/link-stack/zammad:latest`                |
| zammad-opensearch     | `registry.gitlab.com/digiresilience/link/link-stack/opensearch:latest`            |
| zammad-postgresql     | `registry.gitlab.com/digiresilience/link/link-stack/postgresql:latest`            |
| zammad-railsserver    | `registry.gitlab.com/digiresilience/link/link-stack/zammad:latest`                |
| zammad-redis          | `registry.gitlab.com/digiresilience/link/link-stack/redis:latest`                 |
| zammad-scheduler      | `registry.gitlab.com/digiresilience/link/link-stack/zammad:latest`                |
| zammad-websocket      | `registry.gitlab.com/digiresilience/link/link-stack/zammad:latest`                |
