diff --git a/content/momentum/4/new-installation.md b/content/momentum/4/new-installation.md index 81a5c6f98..16a05e63e 100644 --- a/content/momentum/4/new-installation.md +++ b/content/momentum/4/new-installation.md @@ -1,5 +1,5 @@ --- -lastUpdated: "03/26/2020" +lastUpdated: "06/05/2026" title: "New Installation - All Configurations" description: "The installation and upgrade instructions in Chapters 8 through 11 are only applicable in their entirety for Momentum 4 x releases prior to 4 2 28 For release 4 2 28 and beyond please refer to the installation and upgrade PDF documents available under the desired release's folder on the..." --- @@ -7,7 +7,7 @@ description: "The installation and upgrade instructions in Chapters 8 through 11 | **WARNING** | | -- | -| **The installation and upgrade instructions in this section are only applicable in their entirety for Momentum 4.x releases prior to 4.2.28.** For release 4.2.28 and beyond, please refer to the installation and upgrade PDF documents available under the desired release's folder on the Message Systems Support website's [Downloads page](https://support.messagesystems.com/start.php/). If you are uncertain as to which document is applicable to your situation, please contact your technical support representative.| +| **The installation and upgrade instructions in this section are only applicable in their entirety for Momentum 4.x releases prior to 4.2.28.** For Momentum 4.x release 4.2.28 and beyond, please refer to the installation and upgrade PDF documents available under the desired release's folder on the Message Systems Support website's [Downloads page](https://support.messagesystems.com/start.php/). **For Momentum 5.x and later, use the [Online Manuals](/momentum/manuals)** — Installation, Upgrade, and feature-enablement guides, also available as downloadable PDFs in the same website. If you are uncertain as to which document is applicable to your situation, please contact your technical support representative.| This section documents the installation procedures for use in either a local or Amazon Web Services (AWS) environment. This installation can be scaled for a variety of installation configurations, including singlenode, a cluster with three combined Platform and Analytics nodes, and two-tiered topologies that have multiple Platform and multiple, separate Analytics nodes. @@ -464,4 +464,4 @@ Be sure to repeat the steps in this section on **all** remaining platform nodes. /opt/msys/ecelerity/bin/eccfg bootstrap --clustername default -u admin -p $ADMINPASS $INITIALNODE 3. For each remaining MTA node, test Ecelerity configuration exactly as you did on the first MTA node as described in Step #7 of section [“Configure Ecelerity”](/momentum/4/new-installation#install.two_tier.configuration.ecelerity). -4. If not already done so in section [“Configure RabbitMQ”](/momentum/4/new-installation#install.two_tier.configuration.rabbitmq), log onto each remaining MTA node and configure RabbitMQ exactly as you did on the first MTA node as described in that section. \ No newline at end of file +4. If not already done so in section [“Configure RabbitMQ”](/momentum/4/new-installation#install.two_tier.configuration.rabbitmq), log onto each remaining MTA node and configure RabbitMQ exactly as you did on the first MTA node as described in that section. diff --git a/content/momentum/4/p-installing.md b/content/momentum/4/p-installing.md index 030b36fa0..2369fb5ae 100644 --- a/content/momentum/4/p-installing.md +++ b/content/momentum/4/p-installing.md @@ -1,7 +1,11 @@ --- -lastUpdated: "04/03/2020" +lastUpdated: "06/05/2026" title: "Installing Momentum" description: "Table of Contents 5 Hardware Considerations 5 1 Server Roles 5 2 Configuration Options 5 3 Hardware Scaling Approach 5 4 Environmental Considerations 5 5 Hardware Deployment Configuration 5 6 Production Environment Configurations 5 7 Additional Configuration Notes 6 Before You Begin 6 1 Momentum License 6 2 Operating System..." --- +| **IMPORTANT — applies to Momentum 4.x and older** | +| -- | +| The installation and upgrade instructions in this section apply to **Momentum 4.x and earlier** releases and are not maintained for current versions. **For Momentum 5.x and later, use the [Online Manuals](/momentum/manuals)** — Installation, Upgrade, and feature-enablement guides, also available as downloadable PDFs in the [Message Systems Support](https://support.messagesystems.com/start.php/) website. | + Proceed to the following sections for standard installation instructions. diff --git a/content/momentum/manuals/enabling-apis-message-generation.md b/content/momentum/manuals/enabling-apis-message-generation.md new file mode 100644 index 000000000..3ebfae377 --- /dev/null +++ b/content/momentum/manuals/enabling-apis-message-generation.md @@ -0,0 +1,321 @@ +--- +title: "Enabling APIs & Message Generation" +subtitle: "Momentum" +version: "5.3" +date: "June 2026" +description: "How to enable the Transmissions REST API, SMTPAPI (SMTP engagement tracking), and Message Generation on an MTA-only Momentum 5.3 cluster — including msg_gen and smtpapi configuration and the OpenResty NGINX proxy setup." +lastUpdated: "06/05/2026" +--- + +# 1. Purpose + +Following the initial installation of Momentum 5.3 in an MTA-only configuration, the following features can be added and enabled by following this procedure: + +- 5.3 Transmissions REST API +- Message Generation functionalities +- SMTPAPI for SMTP Engagement Tracking + +These procedures can also be performed after an upgrade to Momentum 5.3 if the pre-upgraded installation had not already had them done in the previous version. + +# 2. Important Caveats + +Before 4.2.31, the transmissions API and message generation functionality required the use of the Cassandra database. + +The elimination of Cassandra has the following feature implications, compared to previous releases: + +- Tags are not supported. They will be ignored by the Transmissions API and SMTPAPI. +- Scheduled generation is not supported. Generation times set in the future will be rejected by the Transmissions API. +- The use of stored templates or stored recipient lists is not supported. They will be rejected by the Transmissions API. Consequently, neither the Templates API nor the Recipient Lists API is supported. All transmissions must use an inline recipient(s) and an inline template. +- No authentication, nor support for API keys. The Transmissions API will not be authenticated. + +As mentioned above, the transmissions API is not authenticated in the default installation. Additionally, if you enable the SMTPAPI for Engagement Tracking, you need to allow the click and open HTTP requests to the servers. This may require firewall or external router configuration outside of the scope of this document. For our purposes below, we will assume that port 80 on each MTA is accessible only internally and is used exclusively for the Transmissions API submissions over HTTP. If you are enabling Engagement tracking, you will need to map an external IP and port to the internal port 81, which will be used only for clicks and opens over HTTP. + +> **Note:** Since the Transmissions API does not require authentication and the Engagement Tracking requires inbound connections from the Internet, you are strongly advised to consult your IT support staff to ensure that only internal connections can be made to the Transmissions API and that external connections can only access an alternate listener (e.g., port 81 below). + +# 3. Enable Message Generation + +## 3.1. Update the Configuration on the First Node + +1. Copy the **msg_gen.conf** sample configuration file to the appropriate location. + + ```bash + cp /opt/msys/ecelerity/etc/sample-configs/default/msg_gen.conf \ + /opt/msys/ecelerity/etc/conf/default/ + ``` + +2. In the **/opt/msys/ecelerity/etc/conf/default/msg_gen.conf** file, edit the following lines in the **msg_gen** stanza: + + - Substitute your hostnames as appropriate. + - If this is a single-node install, set **cluster_cfg = false**; otherwise, set it to **true**. + - Engagement Tracking requires an active DNS name to be included in generated messages. This must resolve to a publicly accessible IP address and map to one or more of the Platform hosts. Replace `__EXTERNAL_DNS_HOSTNAME__` with this active DNS name. + - In all configurations below, use your internal hostnames, not necessarily the same as public hostnames (if assigned). + - The **mta_id** must be globally unique across the cluster and should not be changed after being configured initially. + + ``` + msg_gen { + engagement_tracking_host = "__EXTERNAL_DNS_HOSTNAME__:__EXTERNAL_PORT__" + cluster_cfg = + node mta1.yourdomain.com { mta_id = 1 votes = 1 } + node mta2.yourdomain.com { mta_id = 2 votes = 1 } + ... + node mtaN.yourdomain.com { mta_id = n votes = 1 } + } + ``` + +3. Include **msg_gen.conf** in the **/opt/msys/ecelerity/etc/conf/default/ecelerity.conf** configuration file. Remove the comment character (#) from the existing line in `ecelerity.conf`: + + ``` + # include "msg_gen.conf" + ``` + +## 3.2. Configure Remaining MTA Nodes + +1. Copy these config files to each MTA node in the cluster. + + ```bash + scp /opt/msys/ecelerity/etc/conf/default/msg_gen.conf \ + mtaN.yourdomain.com:/opt/msys/ecelerity/etc/conf/default + scp /opt/msys/ecelerity/etc/conf/default/ecelerity.conf \ + mtaN.yourdomain.com:/opt/msys/ecelerity/etc/conf/default + ``` + +2. Restart **ecelerity** on all nodes: + + ```bash + sudo systemctl restart ecelerity + ``` + +# 4. Enable SMTPAPI + +## 4.1. Update the Configuration on the First Node + +1. Copy the **smtpapi.conf** file to the appropriate location: + + ```bash + cp /opt/msys/ecelerity/etc/sample-configs/default/smtpapi.conf \ + /opt/msys/ecelerity/etc/conf/default/ + ``` + +2. Include **smtpapi.conf** in the **/opt/msys/ecelerity/etc/conf/default/ecelerity.conf** configuration file. Remove the comment character (#) from the line: + + ``` + # include "smtpapi.conf" + ``` + +3. Configure your ESMTP listener for outbound traffic to set up engagement tracking. Below is an example to set up a port 8025 listener for outbound traffic with open and click tracking turned on and HTTP links. Update **/opt/msys/ecelerity/etc/conf/default/ecelerity.conf** with the desired changes. This is an alternate injection port to the default port 25 listener. If you want to enable click/open tracking for all messages, add the following lines to the default port 25 stanza instead (everything inside the Listen context). + + ``` + Esmtp_Listener { + Listen ":8025" { + open_tracking_enabled = true + click_tracking_enabled = true + # Change this to your external DNS hostname and external port + # number. This will appear in the tracked links. + tracking_domain = "__EXTERNAL_DNS_HOSTNAME__:__EXTERNAL_PORT__" + # HTTPS is also supported, but requires further configuration + # and a TLS certificate valid for the external hostname. + click_tracking_scheme = "http" + open_tracking_scheme = "http" + } + } + ``` + +4. Test the configuration changes — this should display "OK", or an error message if there is a problem that needs correcting: + + ```bash + /opt/msys/ecelerity/bin/ec_console /tmp/2025 config reload + ``` + +5. Restart **ecelerity**: + + ```bash + sudo systemctl restart ecelerity + ``` + +## 4.2. Configure Remaining MTA Nodes + +1. Copy these config files to each MTA node in the cluster. + + ```bash + scp /opt/msys/ecelerity/etc/conf/default/smtpapi.conf \ + mtaN.yourdomain.com:/opt/msys/ecelerity/etc/conf/default + scp /opt/msys/ecelerity/etc/conf/default/ecelerity.conf \ + mtaN.yourdomain.com:/opt/msys/ecelerity/etc/conf/default + ``` + +2. Restart **ecelerity**: + + ```bash + sudo systemctl restart ecelerity + ``` + +# 5. Setup NGINX + +Momentum uses [OpenResty NGINX](https://openresty.org/en/download.html). For Red Hat Enterprise Linux (RHEL) 8 and 9, the installation steps followed and tested with Momentum are [here](https://openresty.org/en/linux-packages.html#rhel). + +> **Note:** If the procedure for enabling 5.3 Webhooks has already been performed on this cluster, NGINX has already been set up and configured. If that is the case, this entire section can be skipped now, and the procedure is complete. + +> **Note:** By the time of the Momentum 5.3 release, there was no package available for RHEL 10; an **msys-openresty** package can be found at the [Message Systems Support](https://support.messagesystems.com/start.php) website for installation on that specific distro. + +If you have **msys-nginx** installed, please uninstall it first. You can follow the **Upgrade Momentum Webhooks to 5.3** document to upgrade NGINX running on your box. + +## 5.1. Install the Required Package + +For every node you designate as an MTA, make sure OpenResty NGINX is installed. For instance, in a RHEL 9 node: + +```bash +sudo dnf list openresty +``` + +Your results should look like this: + +``` +Installed Packages +openresty.x86_64 1.27.1.2-1.el9 @openresty +``` + +## 5.2. Configure NGINX + +On MTA nodes, an NGINX process acts as an API proxy and load balancer for various API endpoints. In this section, you will configure NGINX for the MTAs. + +> **Note:** OpenResty NGINX has its configuration located under **/usr/local/openresty/nginx**. If yours is different, you can use **sudo find / -name nginx.conf -print** to find the location used by your installation. + +1. Copy the following files. + + ```bash + sudo mkdir -p /usr/local/openresty/nginx/conf.d /var/log/nginx + cd /opt/msys/ecelerity/etc/sample-configs/nginx + sudo cp -i conf/nginx.conf /usr/local/openresty/nginx/conf/ + sudo cp -i conf/logrotate /etc/logrotate.d/openresty + sudo cp -i web_proxy.conf /usr/local/openresty/nginx/conf.d/ + sudo cp -i app.loc /usr/local/openresty/nginx/conf.d/ + sudo cp -i momo.loc /usr/local/openresty/nginx/conf.d/ + sudo cp -i default.cors /usr/local/openresty/nginx/conf.d/ + sudo cp -i momo_upstream.conf /usr/local/openresty/nginx/conf.d/ + # This will ask you if you want to overwrite the destination + # file. It is safe to enter "y" here. It will overwrite a similar + # sample configuration installed by the + # msys-ecelerity-engagement-proxy package. + sudo cp -i click_proxy_upstream.conf /usr/local/openresty/nginx/conf.d/ + ``` + + **If using or planning to enable webhooks**, copy the following file from the NGINX sample-configs: + + ```bash + sudo cp -i api_webhooks_upstream.conf /usr/local/openresty/nginx/conf.d/ + ``` + +2. For each of the following files, edit the associated "upstream stanza" to point to port 2081 on all MTA nodes. See the example below. + + - `/usr/local/openresty/nginx/conf.d/click_proxy_upstream.conf` (port 2081) + - `/usr/local/openresty/nginx/conf.d/momo_upstream.conf` (port 2081) + + Example: + + ``` + cat /usr/local/openresty/nginx/conf.d/click_proxy_upstream.conf + upstream click_proxy { + server mta1.yourdomain.com:2081; + server mta2.yourdomain.com:2081; + ... + server mtaN.yourdomain.com:2081; + least_conn; + } + ``` + +3. **If using or planning to enable webhooks**, edit **/usr/local/openresty/nginx/conf.d/web_proxy.conf**. Remove the comment character (#) from the line: + + ``` + # include "app.loc" + ``` + +4. **If using or planning to enable webhooks**, edit **/usr/local/openresty/nginx/conf.d/api_webhooks_upstream.conf** and add all of your MTA nodes to the **upstream** stanza: + + ``` + upstream api_webhooks { + # List of all Webhooks api instances in the cluster: + server mta1.yourdomain.com:2084; + server mta2.yourdomain.com:2084; + ... + server mtaN.yourdomain.com:2084; + least_conn; + } + ``` + +## 5.3. Enable Click/Open Listener + +If you are enabling the Click/Open listeners, you need to add a Listen stanza permitting NGINX to service the clicks and opens. You can add this to the **/usr/local/openresty/nginx/conf.d/web_proxy.conf** file or add a new **click_tracking.conf** file in the same directory. + +``` +server { + listen 81; # need to be mapped from external port + server_name _; + more_set_headers 'Server: msys-http'; + more_clear_headers 'X-Powered-By'; + location /o { + proxy_pass http://click_proxy; + } + location /q { + proxy_pass http://click_proxy; + } + location /f { + proxy_pass http://click_proxy; + } + location /robots.txt { + access_log off; + add_header Content-Type: text/plain; + return 200 "User-Agent: *\nDisallow: /\n"; + } + location / { + access_log off; + return 404; + more_set_headers "Content-Type: text/plain"; + } +} +``` + +## 5.4. Test the NGINX Configuration + +```bash +sudo openresty -t +``` + +Your results should look like this: + +``` +nginx: the configuration file /usr/local/openresty/nginx/conf/nginx.conf syntax is ok +nginx: configuration file /usr/local/openresty/nginx/conf/nginx.conf test is successful +``` + +## 5.5. Start NGINX + +```bash +sudo systemctl restart openresty +``` + +## 5.6. Configure the Remaining MTA Nodes + +1. Copy the NGINX configuration files to all the remaining MTA nodes. (`$PWD` is a Linux shell variable that always contains the current working directory, i.e., the equivalent of the response from the `pwd` command.) + + ```bash + cd /usr/local/openresty/nginx/conf + scp nginx.conf mtaN.yourdomain.com:$PWD + cd /etc/logrotate.d + scp openresty mtaN.yourdomain.com:$PWD + cd /usr/local/openresty/nginx/conf.d + scp web_proxy.conf mtaN.yourdomain.com:$PWD + scp app.loc mtaN.yourdomain.com:$PWD + scp momo.loc mtaN.yourdomain.com:$PWD + scp default.cors mtaN.yourdomain.com:$PWD + scp momo_upstream.conf mtaN.yourdomain.com:$PWD + scp click_proxy_upstream.conf mtaN.yourdomain.com:$PWD + # and if using webhooks: + scp api_webhooks_upstream.conf mtaN.yourdomain.com:$PWD + ``` + +2. Start NGINX on all the remaining MTA nodes. + + ```bash + # If NGINX was started previously, run restart instead of start + sudo systemctl start openresty + ``` diff --git a/content/momentum/manuals/enabling-webhooks.md b/content/momentum/manuals/enabling-webhooks.md new file mode 100644 index 000000000..ff00a5526 --- /dev/null +++ b/content/momentum/manuals/enabling-webhooks.md @@ -0,0 +1,546 @@ +--- +title: "Enabling Webhooks" +subtitle: "Momentum" +version: "5.3" +date: "June 2026" +description: "How to enable Momentum 5.3 Webhooks on an MTA-only cluster — installing RabbitMQ, Node.js, and OpenResty NGINX, configuring the webhooks API/ETL services and the event hose, plus appendices covering webhook events and configuration options." +lastUpdated: "06/05/2026" +--- + +# 1. Purpose + +Following the initial installation of Momentum 5.3 in an MTA-only configuration, the 5.3 Webhooks functionalities can be added and enabled by following the procedures outlined below. + +As of Momentum 4.2.31 and subsequent releases, webhooks are configured in a JSON configuration file; there is no API for configuring webhooks. + +The webhooks add-on feature is supported in Red Hat Enterprise (RHEL) 8, 9, and 10 environments. These files are provided AS IS, and if you need additional support, please contact your sales representative about moving to SparkPost Cloud. + +# 2. Pre-Requisite Packages + +As of version 5.1, the following **msys** packages were removed from the release bundle and should be replaced with the corresponding system packages: + +- **msys-rabbitmq**: replaced with **rabbitmq-server** +- **msys-erlang**: **msys-rabbitmq** depends on it. Replaced with the **erlang** package installed with **rabbitmq-server** +- **msys-nginx**: replaced with **openresty** + +> **Note:** By the time of the Momentum 5.3 release, there was no official **openresty** available for RHEL 10. Until changed, an **msys-openresty** package can be found at the [Message Systems Support](https://support.messagesystems.com/start.php) website. + +The way Momentum uses RabbitMQ and NGINX to offer webhook services remains the same. + +In version 5.3, the **msys-nodejs** package was also removed from the webhooks bundle and should be replaced with a valid Node.js 24 (LTS version) package from either the system or the official repository. + +# 3. Install the Packages + +## 3.1. Install Pre-Requisite Packages + +### 3.1.1. RabbitMQ / rabbitmq-server + +If you have **msys-rabbitmq** installed, please follow the **Upgrade Momentum Webhooks to 5.3** document to remove **msys-rabbitmq** and install the system-provided RabbitMQ package. + +For a new installation: + +1. Download and install Erlang OTP from GitHub: [https://github.com/rabbitmq/erlang-rpm/releases](https://github.com/rabbitmq/erlang-rpm/releases) + +2. Download and install the common **noarch** package of RabbitMQ server from [https://github.com/rabbitmq/rabbitmq-server/releases](https://github.com/rabbitmq/rabbitmq-server/releases) (at this time, it is still named as **el8**) + +### 3.1.2. rabbitmqadmin + +The deprecated **msys-rabbitmq** includes **rabbitmqadmin**, but the official RabbitMQ package does not include it, and you need to install it separately. Momentum is tested with the **rabbitmqadmin** v2 tool. For a new installation in RHEL 9 x86 and 10, download and install the latest version from GitHub: [https://github.com/rabbitmq/rabbitmqadmin-ng/releases](https://github.com/rabbitmq/rabbitmqadmin-ng/releases). + +For RHEL 8 and RHEL 9 ARM: + +1. Download the source tarball of v2.26.0 (last buildable) from [https://github.com/rabbitmq/rabbitmqadmin-ng/releases/tag/v2.26.0](https://github.com/rabbitmq/rabbitmqadmin-ng/releases/tag/v2.26.0) + +2. Install **cargo** and build **rabbitmqadmin** from source: + + ```bash + tar -zxf v.tar.gz + cd rabbitmqadmin-ng-/ + cargo build --release + ``` + +3. As **root**, copy the **rabbitmqadmin** binary from **target/release** to the system path. + +### 3.1.3. NGINX + +If you have **msys-nginx** installed, please uninstall it first. You can follow the **Upgrade Momentum Webhooks to 5.3** document to upgrade NGINX running on your box. + +Momentum uses [OpenResty NGINX](https://openresty.org/en/download.html). The installation steps followed and tested with Momentum are [here](https://openresty.org/en/linux-packages.html#rhel) for RHEL 8 and 9. For RHEL 10, if there is no official package yet, install the **msys-openresty** package available on the [Message Systems Support](https://support.messagesystems.com/start.php) website. + +### 3.1.4. Node.js + +Momentum 5.3 uses Node.js 24 LTS from external providers. If you have **msys-nodejs** installed, please uninstall it first. + +For RHEL 8 and 9, upstream **nodejs-libs** and **nodejs** RPM packages must be downloaded and installed. In RHEL 10, version 24 is available to be installed as an alternative: + +```bash +sudo dnf install -y nodejs24 +sudo alternatives --install /usr/bin/node node /usr/bin/node-24 100 +sudo alternatives --install /usr/bin/npm npm /usr/bin/npm-24 100 +sudo alternatives --install /usr/bin/npx npx /usr/bin/npx-24 100 +``` + +## 3.2. Install the Webhooks Bundle of Momentum + +1. Download the correct webhooks bundle for your architecture from the [Message Systems Support](https://support.messagesystems.com/start.php) website for every node you will install, and unpack the file. + +2. Copy the bundle to the **/var/tmp/** directory on each of the nodes. _Note that throughout this document, specific bundle filenames are shown, and their resulting install directory names are only examples._ + + ```bash + cp momentum-webhooks-bundle-5.3.x.yyyyy.rhel..tar.gz \ + /var/tmp/ + ``` + + where **\** is **8**, **9**, or **10**, and **\** is **x86_64** or **aarch64** (except to RHEL 8). + +3. Unpack the tarball on each node and set the repository directory. + + ```bash + cd /var/tmp + tar -zxf \ + momentum-webhooks-bundle-5.3.x.yyyyy.rhel..tar.gz + cd momentum-webhooks-5.3.x.yyyyy/ + ./setrepodir + ``` + + > **Note:** The **./setrepodir** script establishes some environmental parameters for the installation. If the installation is not completed within the same terminal session in which it was started, the **./setrepodir** command _must_ be re-executed in any new session(s) before executing any of the **dnf** commands for the installation. + +4. **[MTA Nodes]** For every node you designated as an MTA (including the first node, IF it is NOT a Log Aggregator), perform the following: + + ```bash + sudo dnf install -y --config momentum.repo --enablerepo momentum \ + msys-role-webhooks + ``` + +# 4. Configure RabbitMQ Server + +On all MTA nodes, change to the **root** user: + +```bash +sudo su +``` + +Configure RabbitMQ: + +```bash +systemctl enable rabbitmq-server +systemctl restart rabbitmq-server +rabbitmq-plugins enable rabbitmq_management +rabbitmqadmin declare exchange --name momentum_metrics --type topic +export RABBITMQPWD="p1-Vk0lXy" +rabbitmqctl add_user rabbitmq $RABBITMQPWD +rabbitmqctl set_user_tags rabbitmq administrator +rabbitmqctl set_permissions -p '/' rabbitmq '.*' '.*' '.*' +rabbitmqctl delete_user guest +``` + +_(… continue as root for the instructions below and through section 5.1 …)_ + +Since Momentum 5.1, TLS connections to the RabbitMQ server are supported. To enable TLS in the RabbitMQ server, please refer to [https://www.rabbitmq.com/docs/ssl#enabling-tls](https://www.rabbitmq.com/docs/ssl#enabling-tls). Here is an example of **/etc/rabbitmq/rabbitmq.conf** having TLS enforced: + +``` +# disables non-TLS listeners, only TLS-enabled clients +# will be able to connect +listeners.tcp = none # comment out if allows non-TLS connection +listeners.ssl.default = 5671 +ssl_options.cacertfile = /path/to/cafile +ssl_options.certfile = /path/to/certfile +ssl_options.keyfile = /path/to/privatekeyfile +ssl_options.verify = verify_peer +ssl_options.fail_if_no_peer_cert = true +``` + +RabbitMQ must be configured with a TLS certificate that: + +- Is signed by a **trusted CA** (not self-signed); +- Contains a **Subject Alternative Name (SAN)** entry that matches the hostname clients will use to connect. + +Clients may fail to connect if the certificate is invalid, self-signed, or missing SAN entries. + +# 5. Set Up Webhook Processes on the MTAs + +> **Note:** Whenever you change any **webhooks-api** JSON configuration file, the **msys-app-webhooks-api** service _must_ be restarted for the changes to take effect; likewise, for the **webhooks-etl** JSON configuration files and service. + +## 5.1. Configure One MTA Node + +1. Continuing as the **root** user, edit/create **/opt/msys/app/webhooks-api/config/production.json** on one MTA, as below: + + ```json + { + "application": { + "cassandraSupport": { + "enabled": false + }, + "analyticsSupport": { + "enabled": false + } + } + } + ``` + +2. Create **/opt/msys/app/webhooks-api/config/webhooks.json** on the same MTA. The example below shows several different webhook configurations; the `custom_headers` entry is an example of using the custom headers feature. + + ```json + [ + { + "name": "My example webhook #1", + "events": ["policy_rejection", "bounce", "delay"], + "target": "https://example-domain.com/path/to/endpoint", + "custom_headers": { + "x-api-key": "4194cf7d-f0ce-427c-a026-a11faa6c437e" + } + }, + { + "name": "My example webhook #2", + "events": ["open", "click"], + "target": "https://your-consumer-domain.com/path/to/your/code" + }, + { + "name": "My example webhook #3", + "events": [ + "delivery", "injection", "bounce", "delay", "policy_rejection", + "out_of_band", "open", "click", + "generation_failure", "generation_rejection", "spam_complaint", + "ad_status", "ad_adjust", + "link_unsubscribe", "sms_status" + ], + "target": "https://your-consumer-domain2.com/all/events/path/to/your/code" + } + ] + ``` + +3. Create **/opt/msys/app/webhooks-etl/config/production.json** on the same MTA (notice the **RABBITMQPWD** variable was set during RabbitMQ configuration). + + ```bash + echo "{\"rabbitmq\":{\"password\":\"$RABBITMQPWD\"}}" \ + > /opt/msys/app/webhooks-etl/config/production.json + ``` + + If connecting to RabbitMQ using TLS, **/opt/msys/app/webhooks-etl/config/production.json** should be _added_ with: + + ```json + { + "rabbitmq": { + "password": "p1-Vk0lXy", + "host": "localhost", + "port": 5671, + "ssl": { + "enabled": true, + "caFile": "/path/to/ca.cert", + "certFile": "/path/to/client.cert", + "keyFile": "/path/to/client.key" + } + } + } + ``` + +4. Validate the JSON structure of the created files on the MTA. The **jq** utility displays an error if the JSON file contains syntax errors or displays its contents if it is valid. + + ```bash + jq . /opt/msys/app/webhooks-api/config/production.json + jq . /opt/msys/app/webhooks-api/config/webhooks.json + jq . /opt/msys/app/webhooks-etl/config/production.json + ``` + +5. Restart the **msys-app-webhooks-api** and **msys-app-webhooks-etl** services on the same MTA and check the process logs for errors (use **CTRL-C** to exit the **journalctl -f** of the logs). + + ```bash + systemctl restart msys-app-webhooks-api + systemctl restart msys-app-webhooks-etl + journalctl -u msys-app-webhooks-* -f + exit # (from root) + ``` + +## 5.2. Configure Remaining MTA Nodes + +1. Copy the webhooks configuration files to all of the remaining MTA nodes. (`$PWD` is a Linux shell variable that always contains the current working directory, i.e., the equivalent of the response from the `pwd` command.) + + ```bash + cd /opt/msys/app/webhooks-api/config + scp production.json webhooks.json mtaN.your.domain:$PWD + cd /opt/msys/app/webhooks-etl/config + scp production.json mtaN.your.domain:$PWD + ``` + +2. Log in to each of the remaining MTA nodes in turn, start the Webhooks processes on them, and check the process logs for errors. (Use **CTRL-C** to exit the **journalctl -f** of the logs.) + + ```bash + sudo systemctl restart msys-app-webhooks-api + sudo systemctl restart msys-app-webhooks-etl + sudo journalctl -u msys-app-webhooks-* -f + ``` + +# 6. Enable Event Hydrant & Event Hose on the MTA + +## 6.1. Update Configuration on the First Node + +1. Enable event_hydrant and event_hose in the **/opt/msys/ecelerity/etc/conf/default/ecelerity.conf** configuration file by removing the comment character (#) from the following lines: + + ``` + #event_hydrant "event_collector" { + #} + #event_hose "momentum_metrics" { + #use_tls = 1 # use TLS with the RabbitMQ server + #port = 5671 + #tls_ca = "/path/to/ca.cert" + #tls_certificate = "/path/to/client.cert" + #tls_key = "/path/to/client.key" + #} + ``` + +2. Commit the changes (if you're using **ecconfigd**) and restart Ecelerity: + + ```bash + # $ADMINPASS is the password used during the MTA installation + /opt/msys/ecelerity/bin/eccfg commit --username admin \ + --password $ADMINPASS --add-all --message "Enabling Event Hose" + sudo systemctl restart ecelerity + ``` + +## 6.2. Configure Remaining MTA Nodes + +1. If NOT using **ecconfigd** to manage the configuration, copy the **ecelerity.conf** config file to each MTA node in the cluster. If using **ecconfigd**, these will be propagated automatically. + + ```bash + scp /opt/msys/ecelerity/etc/conf/default/ecelerity.conf \ + mtaN.your.domain:/opt/msys/ecelerity/etc/conf/default + ``` + +2. Restart ecelerity on each of the remaining MTA nodes: + + ```bash + sudo systemctl restart ecelerity + ``` + +# 7. Setup NGINX + +> **Note:** If the procedure for enabling 5.3 REST APIs and Message Generation has already been performed on this cluster, NGINX has already been set up and configured according to the same instructions below. Double-check that each step in those instructions that was prefaced with "If using webhooks…" was executed at that time. If any were not, execute just those steps now. Otherwise, this entire section can be skipped, and the procedure is complete. + +## 7.1. Install the Required Package + +For every node you designate as an MTA, make sure OpenResty NGINX is installed. For instance, in RHEL 8 and 9 nodes (replace with **msys-openresty** if in RHEL 10 nodes): + +```bash +sudo dnf list openresty +``` + +Your results should look like this: + +``` +Installed Packages +openresty.x86_64 1.27.1.2-1.el9 @openresty +``` + +## 7.2. Configure NGINX + +On MTA nodes, an NGINX process acts as an API proxy and load balancer for various API endpoints. In this section, you will configure NGINX for the MTAs. + +> **Note:** OpenResty NGINX has its configuration located under **/usr/local/openresty/nginx**. If yours is different, you can use **sudo find / -name nginx.conf -print** to find the location used by your installation. + +1. Copy the following files: + + ```bash + sudo mkdir -p /usr/local/openresty/nginx/conf.d /var/log/nginx + cd /opt/msys/ecelerity/etc/sample-configs/nginx + sudo cp -i conf/nginx.conf /usr/local/openresty/nginx/conf/ + sudo cp -i conf/logrotate /etc/logrotate.d/openresty + sudo cp -i web_proxy.conf app.loc momo.loc default.cors \ + momo_upstream.conf /usr/local/openresty/nginx/conf.d/ + # This will ask you if you want to overwrite the destination + # file. It is safe to enter "y" here. It will overwrite a similar + # sample configuration installed by the + # msys-ecelerity-engagement-proxy package. + sudo cp -i click_proxy_upstream.conf \ + /usr/local/openresty/nginx/conf.d/ + ``` + + Copy the following file from the NGINX sample-configs as well, for webhooks: + + ```bash + sudo cp -i \ + api_webhooks_upstream.conf /usr/local/openresty/nginx/conf.d/ + ``` + +2. For **each** of the following files, edit the associated upstream stanza to point to port 2081 on all MTA nodes. See the example below. + + - `/usr/local/openresty/nginx/conf.d/click_proxy_upstream.conf` (port 2081) + - `/usr/local/openresty/nginx/conf.d/momo_upstream.conf` (port 2081) + + Example: + + ``` + cat /usr/local/openresty/nginx/conf.d/click_proxy_upstream.conf + upstream click_proxy { + server mta1.your.domain:2081; + server mta2.your.domain:2081; + ... + server mtaN.your.domain:2081; + least_conn; + } + ``` + +3. For webhooks, edit **/usr/local/openresty/nginx/conf.d/web_proxy.conf**. Remove the comment character (#) from the existing line: + + ``` + # include "../conf.d/app.loc" + ``` + +4. For webhooks, edit **/usr/local/openresty/nginx/conf.d/api_webhooks_upstream.conf** and add all of your MTA nodes to the **upstream** stanza: + + ``` + upstream api_webhooks { + # List of all Webhooks api instances in the cluster: + server mta1.your.domain:2084; + server mta2.your.domain:2084; + ... + server mtaN.your.domain:2084; + least_conn; + } + ``` + +## 7.3. Test the NGINX Configuration + +```bash +sudo openresty -t +``` + +Your results should look like this: + +``` +nginx: the configuration file /usr/local/openresty/nginx/conf/nginx.conf syntax is ok +nginx: configuration file /usr/local/openresty/nginx/conf/nginx.conf test is successful +``` + +## 7.4. Start NGINX + +```bash +sudo systemctl restart openresty +``` + +## 7.5. Configure Remaining MTA Nodes + +1. Copy the NGINX configuration files to all of the remaining MTA nodes. (`$PWD` is a Linux shell variable that always contains the current working directory, i.e., the equivalent of the response from the `pwd` command.) + + ```bash + cd /usr/local/openresty/nginx/conf + scp nginx.conf mtaN.yourdomain.com:$PWD + cd /etc/logrotate.d + scp openresty mtaN.yourdomain.com:$PWD + cd /usr/local/openresty/nginx/conf.d + scp web_proxy.conf app.loc momo.loc default.cors \ + momo_upstream.conf click_proxy_upstream.conf \ + mtaN.your.domain:$PWD + # and if using webhooks: + scp api_webhooks_upstream.conf mtaN.your.domain:$PWD + ``` + +2. Start NGINX on all the remaining MTA nodes. + + ```bash + systemctl restart openresty + ``` + +3. Configure webhooks as per the previous example of the **webhooks.json** file and the information in the Appendices. + +# Appendix 1: Webhook Event Reference + +| Event | Event Key (for config file) | Event Description | +| --- | --- | --- | +| Adaptive Delivery Adjustment | `"ad_adjust"` | Momentum changed a low-level parameter as a result of an adaptive rule being triggered or automatic performance tuning | +| Adaptive Delivery Status | `"ad_status"` | A negative ISP response triggered an adaptive rule, and Momentum took action to adjust its sending patterns | +| Bounce | `"bounce"` | Remote MTA has permanently rejected a message | +| Click | `"click"` | The recipient clicked a tracked link in a message, thus prompting a redirect through the Momentum click-tracking server to the link's destination | +| Delay | `"delay"` | Remote MTA has temporarily rejected a message | +| Delivery | `"delivery"` | Remote MTA acknowledged receipt of a message | +| Generation Failure | `"generation_failure"` | Message generation failed for an intended recipient | +| Generation Rejection | `"generation_rejection"` | Momentum rejected message generation due to policy | +| Injection | `"injection"` | The message is received by or injected into Momentum | +| Link Unsubscribe | `"link_unsubscribe"` | The user clicked a hyperlink in a received email | +| List Unsubscribe | `"list_unsubscribe"` | The user clicked the 'unsubscribe' button on an email client | +| Open | `"open"` | The recipient opened a message in a mail client, thus rendering a tracking pixel | +| Out of Band Bounce | `"out_of_band"` | Remote MTA initially reported acceptance of a message, but it has since asynchronously reported that the message was not delivered | +| Policy Rejection | `"policy_rejection"` | Due to policy, Momentum rejected a message or failed to generate a message | +| SMS Status | `"sms_status"` | SMPP/SMS message produced a status log output | +| Spam Complaint | `"spam_complaint"` | The message was classified as spam by the recipient | + +# Appendix 2: Webhooks API Configuration + +`application.analyticsSupport.enabled` + +Used to determine if Vertica is used to store webhook batch status information, or if a flat file is used instead. **This should be set to `false` if your Momentum cluster does not have Vertica installed, which is always the case for an MTA-only fresh install.** Defaults to `true`, for backward compatibility on upgraded systems. + +Example `production.json`: + +```json +{ + "application": { + "analyticsSupport": { + "enabled": false + } + } +} +``` + +`application.analyticsSupport.alternativeLogPath` + +Used to override the default location that the Webhooks API will write batch status information to. Only used if `application.analyticsSupport.enabled` is set to `false`. Defaults to `/var/log/msys-nodejs/webhooks-batch-status.log`. + +Example `production.json`: + +```json +{ + "application": { + "analyticsSupport": { + "enabled": false, + "alternativeLogPath": "/path/to/my/log/file.log" + } + } +} +``` + +# Appendix 3: Webhook Consumers Configuration + +| Config Name | Required/Optional | JSON Data Type | Description | +| --- | --- | --- | --- | +| `name` | optional | String | User-friendly name for the webhook | +| `events` | required | Array | List of events that will be sent via POST request to the target URL | +| `target` | required | String | URL of the target to which to post data | +| `auth_token` | optional | String | Authentication token to present in the `X-MessageSystems-Webhook-Token` header of POST requests to the target URL | +| `custom_headers` | optional | Object | Object of custom headers to be used during POST requests to the target | + +Example `webhooks.json`: + +```json +[ + { + "name": "My example webhook #1", + "events": ["policy_rejection", "bounce", "delay"], + "target": "https://example-domain.com/path/to/endpoint", + "custom_headers": { + "x-api-key": "4194cf7d-f0ce-427c-a026-a11faa6c437e" + } + }, + { + "name": "My example webhook #2", + "events": ["open", "click"], + "target": "https://your-consumer-domain.com/whatever/path/to/your/code" + }, + { + "name": "My example webhook #3", + "events": [ + "delivery", "injection", "bounce", "delay", "policy_rejection", + "out_of_band", "open", "click", + "generation_failure", "generation_rejection", "spam_complaint", + "ad_status", "ad_adjust", + "link_unsubscribe", "sms_status" + ], + "target": "https://your-consumer-domain2.com/all/events/please/path/to/your/code" + } +] +``` + +# Appendix 4: Webhook Usage Configuration + +| Config Name | JSON Data Type | Description | +| --- | --- | --- | +| `disableSingleWorker` | Boolean | If set to `true`, a single webhook will utilize each worker that is spawned. By default, each webhook only uses a single worker. | diff --git a/content/momentum/manuals/images/.gitkeep b/content/momentum/manuals/images/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/content/momentum/manuals/index.md b/content/momentum/manuals/index.md new file mode 100644 index 000000000..df923b8e8 --- /dev/null +++ b/content/momentum/manuals/index.md @@ -0,0 +1,19 @@ +--- +title: "Online Manuals" +description: "Installation, upgrade, and release documentation for on-premises Momentum 5.x and later deployments. Each manual is available online here and as a downloadable, branded PDF generated from the same source." +lastUpdated: "06/05/2026" +--- + +# Online Manuals + +This section holds the long-form manuals shared with on-premises Momentum +customers — installation guides, upgrade guides, and release notes — for +**Momentum 5.x and later releases**. + +## Available manuals + +- [Installation Manual](/momentum/manuals/installation-manual) +- [Upgrade Manual](/momentum/manuals/upgrade-manual) — MTA-only +- [Enabling APIs & Message Generation](/momentum/manuals/enabling-apis-message-generation) — Transmissions API, SMTPAPI, Message Generation +- [Enabling Webhooks](/momentum/manuals/enabling-webhooks) +- [Upgrading Webhooks](/momentum/manuals/upgrading-webhooks) diff --git a/content/momentum/manuals/installation-manual.md b/content/momentum/manuals/installation-manual.md new file mode 100644 index 000000000..5f4c24708 --- /dev/null +++ b/content/momentum/manuals/installation-manual.md @@ -0,0 +1,368 @@ +--- +title: "Installation Manual" +subtitle: "Momentum" +version: "5.3" +date: "June 2026" +description: "How to install Momentum 5.3 on Red Hat Enterprise Linux (RHEL) 8, 9, or 10 — as a single standalone MTA or in a cluster with a Cluster Manager node — including pre-requisites, node configuration, optional PostgreSQL setup, and starting services." +lastUpdated: "06/05/2026" +--- + +# 1. Introduction + +- The Momentum 5.3 configuration is primarily intended for new Momentum on-premise installations in a Red Hat Enterprise Linux (RHEL) operating environment at versions 8, 9, or 10. + +- Following the initial installation of the Momentum 5.3 release configuration, the 5.3 Webhooks functions and/or the REST Transmissions API and Inline Message Generation functions can be added in separate procedures if desired. + + - In 5.3 (as introduced in 4.2.31), these features no longer require the installation of the Cassandra database. Instructions for adding these capabilities are available from Momentum Customer Support. Please contact them before doing the installation if the additional functionality is desired. + +- Installation can be done as a single standalone MTA or in a cluster configuration. In a standalone MTA configuration, there is no log aggregation. In a cluster configuration, a cluster manager is used for cluster-wide log aggregation of files from the MTA nodes. The instructions in this manual will note where they apply to only one of these configurations. + +# 2. Download the Software Bundle + +1. Download the Momentum software bundle from the [Message Systems Support](https://support.messagesystems.com/start.php) website for every node that you will install. + +2. Copy the bundle to the **/var/tmp/** directory on each node. Note that throughout this document, specific bundle filenames are shown, and their resulting install directory names are only examples. + + ```bash + cp momentum-mta-bundle-5.3.x.yyyyy.rhel..tar.gz /var/tmp/ + ``` + + where **\** is **8**, **9**, or **10**, and **\** is **x86_64** or **aarch64** (except to RHEL 8). + + > **Note:** As introduced in 4.4.0, the MTA-only bundles contain all the necessary components for a subsequent enabling of the 5.3 Webhooks and/or the REST APIs and Message Generation after the initial installation. + +3. Unpack the tarball on each node and set the repository directory. + + ```bash + cd /var/tmp + tar -zxf momentum-mta-bundle-5.3.x.yyyyy.rhel..tar.gz + cd momentum-mta-5.3.x.yyyyy/ + ./setrepodir + ``` + + > **Note:** The **./setrepodir** script establishes some environmental parameters for the installation. If the installation is not completed within the same terminal session in which it was started, the **./setrepodir** command _must_ be re-executed in any new session(s) before executing any of the **dnf** commands for the installation. + +4. Your valid Momentum license file will be in each MTA node's **/opt/msys/ecelerity/etc** folder. Your licenses should be pulled automatically once they have been issued. + +5. If your node does not have public internet access during installation, you will need to manually add your valid Momentum license files. + +# 3. Installing Momentum + +## 3.1. Install Pre-Requisite Packages + +Momentum has some dependencies on packages provided by the _Extra Packages for Enterprise Linux_ (EPEL). Make sure it is installed before installing Momentum. + +For installation of EPEL on Red Hat Enterprise Linux (RHEL) 8, 9, and 10 (the **\** number below): + +```bash +sudo rpm -ivh \ + https://dl.fedoraproject.org/pub/epel/epel-release-latest-.noarch.rpm +``` + +Momentum has a dependency on the **leveldb** package as well, but while for RHEL 8 and 9 this package is available through EPEL, in RHEL 10, it needs to be downloaded and installed separately: + +```bash +sudo rpm -ivh \ + https://rpms.remirepo.net/enterprise/10/remi//leveldb-.el10.remi..rpm +``` + +where **\** is the available package version, and **\** is **x86_64** or **aarch64**. + +## 3.2. Installing the MTA Node(s) + +For every node you designate as an MTA, perform the following: + +```bash +cd /var/tmp/momentum-mta-5.3.x.yyyyy +sudo dnf install -y --config momentum.repo --enablerepo momentum msys-role-mta +``` + +> **Note:** If you receive an error saying **libssh2** is needed, proceed with a manual installation of **libssh2**. + +> **Note:** A few utilities under **/opt/msys/ecelerity/bin** may need additional Perl modules, which can be retrieved from CPAN using the command: **sudo cpan install \**. Alternatively, you may be able to find the corresponding RPM package for installation on your platform. + +## 3.3. [OPTIONAL] Installing Cluster Manager Node + +> **Note:** Skip this whole section for single-node installation. + +In a fully clustered environment, you can designate a dedicated host as the Cluster Manager. This node can then: + +- Consolidate all of the MTA logs (acting as a "Log Aggregator") + +- Act as one of the MSGC server members to ensure that clustered metrics are maintained + +- (If some of these Momentum features - mostly legacy - are intended to be used: web console/UI; stats producer; mobility; seedlist/ReturnPath integration, then the Cluster Manager is the node where you run the shared PostgreSQL database) + +### 3.3.1. Install PostgreSQL + +For Red Hat Enterprise Linux (RHEL) 10, install the package provided by the standard repositories, then set up the database server: + +```bash +sudo dnf install -y postgresql +sudo /usr/bin/postgresql-setup initdb +sudo systemctl enable --now postgresql +``` + +For RHEL 8 and 9, the packages supplied by the standard repositories are for versions no longer supported, so it is recommended to install a valid package from the official PostgreSQL repository: + +```bash +sudo dnf install -y \ + https://download.postgresql.org/pub/repos/yum/reporpms/EL--/pgdg-redhat-repo-latest.noarch.rpm +sudo dnf -qy module disable postgresql +sudo dnf install -y postgresql16-server +sudo /usr/pgsql-16/bin/postgresql-16-setup initdb +sudo systemctl enable --now postgresql-16 +export PATH=/usr/pgsql-16/bin:$PATH # e.g. for psql +``` + +where **\** is **8** or **9**, and **\** is **x86_64** or **aarch64** (only for RHEL 9). + +### 3.3.2. Set Up the Cluster Manager + +Install these packages on the Cluster Manager node: + +```bash +cd /var/tmp/momentum-mta-5.3.x.yyyyy +sudo dnf install -y --config momentum.repo --enablerepo momentum \ + msys-role-manager msys-role-db msys-ecelerity-mobility-db +``` + +# 4. Configuring the Nodes + +## 4.1. Common Configuration + +Momentum offers numerous configuration options, allowing for the customization of behavior to suit a wide range of needs. The **sample-configs/default** directory contains a basic set of options that will allow the basic services to start. For every node, regardless of whether it is the Cluster Manager or an MTA, copy the required files from the **sample-configs/default** directory to the active configuration directory. These can be adjusted as required once the initial installation is completed. + +```bash +sudo -u ecuser mkdir -p /opt/msys/ecelerity/etc/conf/default +cd /opt/msys/ecelerity/etc/sample-configs/default +sudo -u ecuser cp {ecelerity.conf,ecelerity-cluster.conf,common.conf} \ + /opt/msys/ecelerity/etc/conf/default/ +``` + +## 4.2. License + +Your valid Momentum license file will be in each node's **/opt/msys/ecelerity/etc** folder. Your licenses should be pulled automatically once they have been issued. You can also run the following command to pull your license: + +```bash +/opt/msys/ecelerity/bin/ec_lic -f +``` + +If your node does not have public internet access during installation, you will need to manually add your valid Momentum license files. + +> **Note:** Depending on whether FIPS mode is enabled and which versions of the FIPS modules are running in your environment, the Momentum 5.3 license may be constrained to be signed by specific methods. If the regular license file is not validated under such restrictions, please contact the Momentum Customer Support to obtain a proper license file for your case. + +## 4.3. MTA Configuration + +### 4.3.1. [OPTIONAL] Set Up the Adaptive Live Update + +Adaptive's **lu_pull** utility has a configuration file that controls automatic updates to **adaptive_rules.lua** and bounces classification files. If Momentum is configured to use adaptive, and there's no **liveupdate.conf** file in **/opt/msys/ecelerity/etc**, create one from the **sample-config**: + +```bash +cd /opt/msys/ecelerity/etc/sample-configs +sudo -u ecuser cp liveupdate.conf /opt/msys/ecelerity/etc/ +``` + +## 4.4. [OPTIONAL] Cluster Configuration + +> **Note:** Skip this whole section for single-node installation. + +### 4.4.1. Basic Configuration + +1. Copy the **eccluster.conf** file from the **sample-configs/default** directory: + + ```bash + cd /opt/msys/ecelerity/etc/sample-configs/default + sudo -u ecuser cp eccluster.conf /opt/msys/ecelerity/etc/conf/default/ + ``` + +2. Copy the **msgc_server.conf** file to the appropriate location on the Cluster Manager node: + + ```bash + sudo -u ecuser mkdir -p /opt/msys/ecelerity/etc/conf/global + sudo -u ecuser cp \ + /opt/msys/ecelerity/etc/sample-configs/default/msgc_server.conf \ + /opt/msys/ecelerity/etc/conf/global/ + ``` + +### 4.4.2. [OPTIONAL] Configure the Log Aggregation + +1. Edit **/opt/msys/ecelerity/etc/conf/global/msgc_server.conf.** For EVERY node in the cluster (i.e., the Cluster Manager and all MTAs) you are installing, substitute the hostnames and IP addresses as appropriate. + + ``` + msgc_server { + peers = [ + mgr.yourdomain.com = "10.77.0.219" + mta1.yourdomain.com = "10.77.1.6" + mta2.yourdomain.com = "10.77.1.8" + ... + mtaN.yourdomain.com = "10.77.1.10" + ] + } + ``` + +2. Remove the aggregator comment prefix (**#aggr#**) wherever it occurs in the **/opt/msys/ecelerity/etc/conf/default/ecelerity-cluster.conf** configuration file. + + ``` + #aggr# ec_logger "ec_logger_cluster" { + #aggr# mainlog = "cluster:///var/log/ecelerity/mainlog.cluster=>master" + #aggr# paniclog = "cluster:///var/log/ecelerity/paniclog.cluster=>master" + #aggr# rejectlog = "cluster:///var/log/ecelerity/rejectlog.cluster=>master" + #aggr# acctlog = "cluster:///var/log/ecelerity/acctlog.cluster=>master" + #aggr# } + #aggr# bounce_logger "bounce_logger_cluster" { + #aggr# bouncelog = "cluster:///var/log/ecelerity/bouncelog.cluster=>master" + #aggr# } + (...) + cluster { + logs = [ + #aggr# rejectlog = "/var/log/ecelerity/rejectlog.cluster" + #aggr# paniclog = "/var/log/ecelerity/paniclog.cluster" + #aggr# mainlog = "/var/log/ecelerity/mainlog.cluster" + #aggr# acctlog = "/var/log/ecelerity/acctlog.cluster" + #aggr# bouncelog = "/var/log/ecelerity/bouncelog.cluster" + ] + } + ``` + +3. For EACH MTA node in the cluster, copy both **msgc_server.conf** and **ecelerity-cluster.conf** files from the Cluster Manager node. + + ```bash + sudo -u ecuser mkdir -p /opt/msys/ecelerity/etc/conf/global + cd /opt/msys/ecelerity/etc/conf/ + scp mgr.yourdomain.com:/opt/msys/ecelerity/etc/conf/global/msgc_server.conf \ + global/ + scp \ + mgr.yourdomain.com:/opt/msys/ecelerity/etc/conf/default/ecelerity-cluster.conf \ + default/ + ``` + +4. For EVERY node in the cluster (the Cluster Manager and all MTAs), include the cluster configuration file by removing the comment character (#) from this line in **ecelerity.conf**: + + ``` + # include "ecelerity-cluster.conf" + ``` + +### 4.4.3. [OPTIONAL] Configure the PostgreSQL Database Server + +1. **(IMPORTANT!)** For this particular setup, change to the **root** user in the Cluster Manager node. + + ```bash + sudo su + ``` + +2. Install PostgreSQL in the Cluster Manager node following the instructions of section 3.3.1 (may have already been installed due to former dependencies). + +3. Install the database schema provided by Momentum: + + ```bash + cd /var/tmp/momentum-mta-5.3.x.yyyyy + dnf install -y --config momentum.repo --enablerepo momentum \ + msys-ecelerity-schema + ``` + +4. Create a random service password file named **.svcpasswd** that can be used by the various services to access the database. Additionally, logging into the console remotely requires an additional username/password combination. By convention, this user is **admin**, but you can use any username you want. + + ```bash + mkdir -p /opt/msys/etc + < /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c8 >/opt/msys/etc/.svcpasswd + export SVCPASSWD=`cat /opt/msys/etc/.svcpasswd` + export ADMINPASS=admin + ``` + +5. Create the PostgreSQL database host file. Momentum requires a PostgreSQL database for console authentication and other minor modules. This is a very low-usage database that is installed on the Cluster Manager for simplicity in cluster installations. + + ```bash + MyHostName=`hostname -f` + echo $MyHostName > /opt/msys/etc/.dbhost + ``` + +6. Configure PostgreSQL with the "ecelerity" schema. + + ```bash + systemctl restart postgresql # or postgresql-16 in RHEL 8/9 + sleep 40 + cd /opt/msys/ecelerity/etc + ../bin/init_schema --password $SVCPASSWD --admin-password $ADMINPASS + ``` + + You should see something like this: + + ``` + Loading sql/common.sql into common...done! + Loading sql/console.sql into console...done! + Loading sql/returnpath.sql into returnpath...done! + Loading sql/seedlist.sql into seedlist...done! + Loading sql/adaptive.sql into adaptive...done! + ``` + +7. **Only if** there were any problems creating the "ecelerity" database in PostgreSQL, invoke the following command: + + > **Note:** DO NOT run this command if there were no errors when executing **init_schema** in the previous step. + + ```bash + /opt/msys/3rdParty/bin/dropdb -U ecuser ecelerity + ``` + + Then try to rerun **init_schema**. + +8. Create a **/opt/msys/ecelerity/etc/conf/default/ecdb.conf** file with the database source information. + + ```bash + export MyHostName=`cat /opt/msys/etc/.dbhost` + export SVCPASSWD=`cat /opt/msys/etc/.svcpasswd` + cat << EOT > /opt/msys/ecelerity/etc/conf/default/ecdb.conf + Datasource "ecdb" { uri = ( "pgsql:host=$MyHostName;dbname=ecelerity;user=ecuser;password=$SVCPASSWD" )} + EOT + ``` + +9. Add the following line to the **/opt/msys/ecelerity/etc/conf/default/eccluster.conf** file: + + ``` + readonly_include "ecdb.conf" + ``` + + > **Note:** Make sure to add the line AFTER the inclusion of **common.conf** file, otherwise the database configuration in **ecdb.conf** may be overloaded by **common.conf**. + +10. Copy the **ecdb.conf** file to all MTA nodes in the cluster. + +11. Add this line to the **/opt/msys/ecelerity/etc/conf/default/ecelerity.conf** configuration file on EACH MTA node. + + ``` + readonly_include "ecdb.conf" + ``` + +12. Remove (or comment out) these lines from the **common.conf** file in EVERY node: + + ``` + # Datasource "ecdb" { + # uri = ( "pgsql:dbname=ecelerity;user=ecuser" ) + # } + ``` + + Otherwise, they can inadvertently overload the settings from **ecdb.conf**. + +13. Test the Momentum configuration in EVERY node (the Cluster Manager and all MTAs). Enter the username admin and your $ADMINPASS password when prompted. After logging in, you will get a ">" prompt and will be able to type various commands, such as "version" and "summary". + + ```bash + /opt/msys/ecelerity/bin/ec_console shim:// + # Login with admin/$ADMINPASS. + # (...) + quit + ``` + +# 5. Start Momentum Services + +Depending on the node's role, start the relevant service. + +- If the node is the Cluster Manager: + + ```bash + sudo systemctl start eccmgr + ``` + +- If the node is an MTA: + + ```bash + sudo systemctl start ecelerity + ``` diff --git a/content/momentum/manuals/upgrade-manual.md b/content/momentum/manuals/upgrade-manual.md new file mode 100644 index 000000000..9c671eb6b --- /dev/null +++ b/content/momentum/manuals/upgrade-manual.md @@ -0,0 +1,297 @@ +--- +title: "Upgrade Manual (MTA-Only)" +subtitle: "Momentum" +version: "5.3" +date: "June 2026" +description: "How to upgrade an MTA-only Momentum installation to 5.3 on Red Hat Enterprise Linux (RHEL) 8, 9, or 10 — stopping services, removing legacy packages, upgrading the software and PostgreSQL, migrating NGINX to OpenResty, and restarting services." +lastUpdated: "06/05/2026" +--- + +# 1. Introduction + +This document describes upgrading an MTA-only installation to Momentum 5.3, from the previous versions listed below: + +- Momentum 4.2.38+, 4.3.x, 4.4.x, or 4.6, if the prior installation is in a Red Hat Enterprise Linux (RHEL) 7 (or its CentOS equivalent) operating environment; + + - _Upgrading the operating system to RHEL 8, 9, or 10 is a prerequisite for upgrading Momentum to version 5.3._ If that is not possible, you'll need to set up a new server, then refer to the **Momentum 5.3 Installation** document. + + - If you're running a version _before_ 4.2.38 and have Cassandra enabled, please contact your support representative for assistance. + +- Momentum 4.5, installed on RHEL 8; + +- Momentum 4.7, 4.8, 5.0, 5.1, or 5.2 installed on RHEL 8, 9, or 10 operating environment. + +# 2. Upgrade Instructions + +## 2.1. Download the Software Bundle + +1. Download the Momentum software bundle from the [Message Systems Support](https://support.messagesystems.com/start.php) website for every node that you will upgrade. + +2. Copy the bundle to the **/var/tmp/** directory on each node. _Note that throughout this document, specific bundle filenames are shown, and their resulting install directory names are only examples._ + + ```bash + cp momentum-mta-bundle-5.3.x.yyyyy.rhel..tar.gz \ + /var/tmp/ + ``` + + where **\** is **8**, **9**, or **10**, and **\** is **x86_64** or **aarch64** (except to RHEL 8). + + > **Note:** As introduced in 4.4.0, the MTA-only bundles contain all the necessary components for a subsequent enabling of the 5.3 Webhooks and/or the REST APIs and Message Generation after the initial installation. + +3. Unpack the tarball on each node and set the repository directory. + + ```bash + cd /var/tmp + tar -zxf momentum-mta-bundle-5.3.x.yyyyy.rhel..tar.gz + cd momentum-mta-5.3.x.yyyyy/ + ./setrepodir + ``` + + > **Note:** The **./setrepodir** script establishes some environmental parameters for the installation. If the installation is not completed within the same terminal session in which it was started, the **./setrepodir** command _must_ be re-executed in any new session(s) before executing any of the **dnf** commands for the installation. + +4. Your valid Momentum license file will be in each MTA node's **/opt/msys/ecelerity/etc** folder. Your licenses should be pulled automatically once they have been issued. + +5. If your node does not have public internet access during installation, you will need to manually add your valid Momentum license files. + +> **Note:** Depending on whether FIPS mode is enabled and which versions of the FIPS modules are running in your environment, the Momentum 5.3 license may be constrained to be signed by specific methods. If the regular license file is not validated under such restrictions, please contact the Momentum Customer Support to obtain a proper license file for your case. + +## 2.2. Install EPEL + +Momentum has some dependencies on packages provided by the _Extra Packages for Enterprise Linux_ (EPEL). Make sure it is installed before upgrading Momentum. + +For installation of EPEL on Red Hat Enterprise Linux (RHEL) 8, 9, and 10 (the **\** number below): + +```bash +sudo dnf install -y wget +wget \ + https://dl.fedoraproject.org/pub/epel/epel-release-latest-.noarch.rpm +sudo rpm -Uvh epel-release-latest-.noarch.rpm +``` + +## 2.3. Upgrade Software + +> **Note:** If you are upgrading a **single-node** installation, perform all the steps described in this document for both the first node and subsequent nodes of a cluster. + +The first step is to shut down the relevant services on the nodes to allow for the upgrade. + +### 2.3.1. Stopping first-node services + +If the first node in the cluster is a Cluster Manager, then it is where the **eccmgr** service runs. The node may be a Log Aggregator or a PostgreSQL Database Server. + +```bash +# Stopping eccmgr is only applicable +# if the first node is a Cluster Manager. +sudo systemctl stop eccmgr +``` + +It may be possible that the (now deprecated) Configuration Manager is running in the cluster. If that is the case, **ecconfigd** must be shut down as well. + +```bash +# Stopping ecconfigd is required for all +# first node types, MTA or Cluster Manager. +sudo systemctl stop ecconfigd +``` + +### 2.3.2. Stopping other services + +If you are upgrading a cluster that has already had the optional Webhooks functionality or enabled the Transmissions or SMTP APIs (and engagement tracking), there are additional services to stop. + +If **msys-nginx** exists from a previous installation of Momentum 5.0 or earlier, you need to save the configuration, then remove it before installing OpenResty NGINX. + +1. Backup existing NGINX configurations: + + ```bash + mkdir /var/tmp/nginx + sudo cp -a /opt/msys/3rdParty/nginx/conf* /var/tmp/nginx + ``` + +2. Stop, then _uninstall_ **msys-nginx** (this will remove a bunch of dependencies): + + ```bash + sudo systemctl stop msys-nginx + sudo dnf remove msys-nginx + ``` + +3. Install the **openresty** package by following the steps [here](https://openresty.org/en/linux-packages.html#rhel). + + > **Note:** By the time of the Momentum 5.3 release, there was no official **openresty** available for RHEL 10. Until changed, an **msys-openresty** package can be found at the [Message Systems Support](https://support.messagesystems.com/start.php) website. + +Finally, refer to the **Upgrade Momentum Webhooks to 5.3** document to remove any existing **msys-rabbitmq** and/or **msys-nodejs** packages and install the corresponding upstream ones. + +### 2.3.3. Stopping MTA services + +Perform this step on all MTAs, including the first node if it is _not_ a Cluster Manager. + +```bash +sudo systemctl stop ecelerity +``` + +Perform additional service shutdowns depending on whether webhooks and/or message generation features were enabled after the initial prior version install. Perform these on **all** MTAs, including the first node if it is not a Cluster Manager. See below for these (and other) optional service instructions. + +Lastly, in all nodes, regardless of whether they had the additional services enabled or not, make sure to shut down the Riak service for the distributed database, in case it is running. + +```bash +sudo systemctl stop msys-riak +``` + +### 2.3.4. [OPTIONAL] Prepare for PostgreSQL conversion + +If **msys-pg** exists from a previous installation of Momentum 5.1 or earlier, a conversion will be needed to preserve the database contents. The database files will also reside in a different location. To prepare for this conversion, run the following commands as **root** _before the upgrade._ + +```bash +/opt/msys/3rdParty/bin/pg_dumpall -c -U msyspg \ + -f /var/tmp/pgdumpall.out +/etc/init.d/msyspg stop +``` + +This file must then be edited. Remove all lines containing the string **msyspg** as they are no longer needed. + +### 2.3.5. Upgrade Momentum + +As of version 4.7, many **msys** packages were removed in favor of using system packages. If you're upgrading from a Momentum version before 4.7, it's necessary to remove those packages that are no longer needed. + +```bash +sudo dnf remove -y cassandra-cpp-driver msys-ecelerity-aws-aws \ + msys-aws-cpp-sdk msys-cassandra-python-driver msys-curl erlang \ + msys-ffi msys-freetds msys-gmp msys-gnutls msys-hiredis \ + msys-jemalloc msys-jlog-perl msys-ldap msys-leveldb msys-libevent \ + msys-libmcrypt msys-libstrl msys-libxml2 msys-lua-aws \ + msys-lua-bitop msys-nettle msys-nghttp2 msys-opendkim msys-pcre \ + msys-sqlite msys-unbound msys-unixODBC + +cd /var/tmp/momentum-mta-5.3.x.yyyyy/ +./setrepodir +sudo dnf upgrade -y --config momentum.repo --enablerepo momentum +``` + +After upgrading, if using NGINX: + +1. Restore existing configuration files: + + > **Note:** OpenResty NGINX has its configuration and log files located under **/usr/local/openresty/nginx**. If yours is different, you can use **find / -name nginx.conf -print** to find the location used by your installation. We'll refer to this NGINX root directory as **$NGINX_ROOT** in the next steps. + + ```bash + sudo cp -a /var/tmp/nginx/conf* $NGINX_ROOT + ``` + +2. Edit the **nginx.conf** file in **$NGINX_ROOT** as follows: + + ``` + # Remove or comment out (disable) this line if it exists + #pid /var/run/nginx.pid; + # Set the correct root directory where conf.d can be found + include $NGINX_ROOT/conf.d/*.conf + ``` + +3. Check the configuration is ok: + + ```bash + sudo openresty -t + ``` + +4. Start the NGINX service: + + ```bash + sudo systemctl start openresty + # make sure NGINX is running + ps -ef | grep nginx + ``` + +### 2.3.6. [OPTIONAL] Upgrading the PostgreSQL database server + +As of version 5.2, the **msys-pg** package was replaced with an official version of the PostgreSQL package. In RHEL 10, install the system-provided **postgresql** package: + +```bash +sudo dnf install -y postgresql +``` + +On the other hand, for RHEL 8 and 9, the provided packages are for versions no longer supported. It is then recommended to install the package available from the PostgreSQL repository for a version still under maintenance (e.g., 16): + +```bash +sudo dnf install -y \ + https://download.postgresql.org/pub/repos/yum/reporpms/EL--/pgdg-redhat-repo-latest.noarch.rpm +sudo dnf -qy module disable postgresql +sudo dnf install -y postgresql16-server +sudo /usr/pgsql-16/bin/postgresql-16-setup initdb +sudo systemctl enable --now postgresql-16 +export PATH=/usr/pgsql-16/bin:$PATH # e.g. for psql +``` + +where **\** is **8** or **9**, and **\** is **x86_64** or **aarch64** (only for RHEL 9). + +After the installation: + +1. Restore the contents from the prior installation (of **msys-pg**). + + ```bash + sudo psql -f /var/tmp/pgdumpall.out -h 127.0.0.1 postgres -U postgres + ``` + +2. **Note:** You can set the server location to avoid the need to use the **-h** option for **psql** commands. + + ```bash + export PGHOST=127.0.0.1 + ``` + +3. After correct operation is confirmed, it is recommended to remove the old package if manual operations on the database might be done, to prevent confusion (e.g., inadvertent use of the wrong **psql** utility and database). The old database at **/var/db/msyspg** could also be deleted. + + ```bash + sudo dnf remove msys-pg + ``` + +## 2.4. Starting Momentum services + +Perform the steps of this section on all MTAs, including the first node if it is not a Cluster Manager. + +```bash +sudo systemctl start ecelerity +``` + +If the node is the Cluster Manager: + +```bash +sudo systemctl start eccmgr +``` + +Perform additional service startups depending on whether webhooks and/or message generation features were enabled after the initial prior version install. Perform these on **all** MTAs, including the first node if it is _not_ a Cluster Manager. + +### 2.4.1. Installing the eccfg client code + +If you are still using the **ecconfigd**/**eccfg** service to manage your configuration, you will need to install an additional package: + +```bash +sudo dnf install -y --disablerepo=* --config momentum.repo \ + --enablerepo=momentum msys-ecelerity-config-client +``` + +> **Note:** Configuration Management (**ecconfigd/eccfg** services) has been a deprecated feature since version 4.3.1 of Momentum. + +## 2.5. Additional Notes on NGINX + +The directory mappings between Momentum 5.1+ and previous versions are listed in the table below: + +| Directories in Momentum 5.0 and earlier | Directories in Momentum 5.1 and later | +| --- | --- | +| **/opt/msys/3rdParty/nginx** | **$NGINX_ROOT** | + +The following directories and files that were installed in **/opt/msys/3rdParty/nginx** when **msys-ecelerity-engagement-proxy** was installed are now installed in **/opt/msys/ecelerity/etc/sample-configs/nginx** _and need to be manually copied to **$NGINX_ROOT**_: + +| Installed by Momentum 5.0 and earlier | Installed by Momentum 5.1 and later | Destination of the manual copy | +| --- | --- | --- | +| **/opt/msys/3rdParty/nginx/conf.d/filter_forwarded4.lua** | **/opt/msys/ecelerity/etc/sample-configs/nginx/conf.d/filter_forwarded4.lua** | **$NGINX_ROOT/conf.d/filter_forward** | +| **/opt/msys/3rdParty/nginx/conf.d/click_proxy.conf** | **/opt/msys/ecelerity/etc/sample-configs/nginx/conf.d/click_proxy.conf** | **$NGINX_ROOT/conf.d/click_proxy.conf** | +| **/opt/msys/3rdParty/nginx/conf.d/click_proxy/base.loc** | **/opt/msys/ecelerity/etc/sample-configs/nginx/conf.d/click_proxy/base.loc** | **$NGINX_ROOT/conf.d/click_proxy/base.loc** | + +# 3. Copy Configuration and Spool from an Old Server + +1. Copy over the config (e.g., contents of **/opt/msys/ecelerity/etc/conf**). + +2. If there are still spooled messages on the old server, they can be imported into the new server. After copying the directories containing the spool files (e.g., **/var/spool/ecelerity**) to the new server, use the spool import command — [https://support.sparkpost.com/momentum/4/console-commands/spool-import](https://support.sparkpost.com/momentum/4/console-commands/spool-import). + +3. If desired and applicable, copy the PostgreSQL database by following the instructions above. + +4. If IP addresses are new, adaptive delivery will adjust the sending rate accordingly. But if the IP addresses on the new server are already warmed up, we recommend setting the AD age to a high value so the message rate is not throttled. + +5. Check the release notes and/or changelogs in case some older features are deprecated in the newer releases. + +6. Point the injecting application(s) to the new server. diff --git a/content/momentum/manuals/upgrading-webhooks.md b/content/momentum/manuals/upgrading-webhooks.md new file mode 100644 index 000000000..359302412 --- /dev/null +++ b/content/momentum/manuals/upgrading-webhooks.md @@ -0,0 +1,226 @@ +--- +title: "Upgrading Webhooks" +subtitle: "Momentum" +version: "5.3" +date: "June 2026" +description: "How to upgrade an existing Momentum Webhooks installation to 5.3 on Red Hat Enterprise Linux (RHEL) 8, 9, or 10 — stopping services, upgrading RabbitMQ, NGINX/OpenResty, and Node.js, and reinstalling the 5.3 webhooks packages." +lastUpdated: "06/05/2026" +--- + +# 1. Introduction + +This document applies to the Red Hat Enterprise Linux (RHEL) environments in versions 8, 9, and 10 installed with previous versions of the Momentum Webhooks service, where Webhooks is intended to be upgraded to version **5.3**. + +> **Note:** All the steps in this document have to be performed as the **root** user, and in every MTA node where the Webhooks services are running. + +# 2. Pre-Requisite Packages + +As of version 5.1, the following **msys** packages were removed from the release bundle and should be replaced by corresponding system packages: + +- **msys-rabbitmq**: replaced by **rabbitmq-server** +- **msys-erlang**: **msys-rabbitmq** depends on it. Replaced by the **erlang** package installed with **rabbitmq-server** +- **msys-nginx**: replaced by **openresty** + +The way Momentum uses RabbitMQ and NGINX to offer webhook services remains the same. + +In version 5.3, the **msys-nodejs** package was also removed from the webhooks bundle and should be replaced with a valid Node.js 24 (LTS version) package from either the system or the official repository. + +# 3. Stop Webhooks Services + +Stop the **msys-app-webhooks-\*** services: + +```bash +systemctl stop msys-app-webhooks-* +``` + +> **Note:** Depending on your **msys-app-webhooks-etl** version, only the **msys-app-webhooks-api** could be stopped. In this case, the **msys-app-webhooks-etl** service has to be manually killed, and the PID file in **/run/msys-app** removed. + +# 4. Upgrade to 3rd-Party Packages + +## 4.1. RabbitMQ + +Momentum Webhooks 5.0 and earlier versions run **msys-rabbitmq-3.11.x**. For an in-place upgrade to Webhooks 5.3, RabbitMQ needs to be upgraded to **3.13.x** first, then upgraded to **4.2.x**, and from there to later versions. + +The instructions below describe how to perform the first upgrade step (from **msys-rabbitmq-3.11.x** to upstream **3.13.x**); please refer to the documentation [here](https://www.rabbitmq.com/docs/upgrade#rabbitmq-version-upgradability) for RabbitMQ later versions' upgradeability. + +1. Enable all RabbitMQ feature flags (if you forget to do this step, you must do it when you upgrade to **3.13.x**): + + ```bash + # if rabbitmq-server is not running + systemctl start rabbitmq-server + # enable all feature flags + rabbitmqctl enable_feature_flag all + ``` + +2. Stop the **rabbitmq-server** service: + + ```bash + systemctl stop rabbitmq-server + ``` + +3. _Uninstall_ the prior Momentum Webhooks package: + + ```bash + dnf remove msys-role-webhooks + ``` + +4. Follow the steps [here](https://packagecloud.io/cloudamqp/rabbitmq/packages/el/8/rabbitmq-server-3.13.4-1.el8.noarch.rpm?distro_version_id=205) to upgrade to 3rd-party RabbitMQ **3.13.x**: + + ```bash + curl -s \ + https://packagecloud.io/install/repositories/cloudamqp/rabbitmq/script.rpm.sh + dnf install rabbitmq-server-3.13.x-y.el8.noarch + ``` + +5. Check that the upgraded **rabbitmq-server** can start, then stop it: + + ```bash + # check service start + systemctl start rabbitmq-server + # enable all feature flags (if you didn't do it in step 1 above) + rabbitmqctl enable_feature_flag all + # stop the service + systemctl stop rabbitmq-server + ``` + +6. Upgrade to the latest version of 3rd-party RabbitMQ: + + 1. Download and install Erlang OTP from GitHub: [https://github.com/rabbitmq/erlang-rpm/releases](https://github.com/rabbitmq/erlang-rpm/releases) + 2. Download and install the common **noarch** package of RabbitMQ server from [https://github.com/rabbitmq/rabbitmq-server/releases](https://github.com/rabbitmq/rabbitmq-server/releases) (at this time, it is still named as **el8**) + +7. Check you have the latest versions of Erlang and RabbitMQ, and **rabbitmq-server** can start without any issue: + + ```bash + # confirm RabbitMQ and Erlang versions + rpm -qa | grep -E 'rabbitmq-server|erlang' + # restart RabbitMQ + systemctl restart rabbitmq-server + # verify previously added RabbitMQ users exist + rabbitmqctl list_users + ``` + + There is no change in the locations of RabbitMQ configuration and log files. + +## 4.2. NGINX + +As of version 5.1, Momentum uses OpenResty NGINX. If **msys-nginx** exists from a previous installation of Momentum 5.0 or earlier, you need to remove it before installing OpenResty. + +1. Backup existing NGINX configurations: + + ```bash + mkdir /tmp/nginx + cp -a /opt/msys/3rdParty/nginx/conf* /tmp/nginx + ``` + +2. Stop, then _uninstall_ **msys-nginx**: + + ```bash + systemctl stop msys-nginx + dnf remove msys-nginx + ``` + +3. Install OpenResty by following the steps [here](https://openresty.org/en/linux-packages.html#rhel). + +4. Restore existing configuration files: + + > **Note:** OpenResty NGINX has its configuration and log files located under **/usr/local/openresty/nginx**. If yours is different, you can use **find / -name nginx.conf -print** to find the location used by your installation. We'll refer to this NGINX root directory as **$NGINX_ROOT** in the next steps. + + ```bash + cp -a /tmp/nginx/conf* $NGINX_ROOT + ``` + +5. Edit the **nginx.conf** file in **$NGINX_ROOT** as follows: + + ``` + # Remove or comment out (disable) this line if it exists + #pid /var/run/nginx.pid; + # Set the correct root directory where conf.d can be found + include $NGINX_ROOT/conf.d/*.conf + ``` + +6. Check the configuration is ok: + + ```bash + openresty -t + ``` + +7. Start the NGINX service: + + ```bash + systemctl start openresty + # make sure NGINX is running + ps -ef | grep nginx + ``` + +### 4.2.1. Additional Notes on NGINX + +The directory mappings between Momentum 5.1+ and previous versions are listed in the table below: + +| Directories in Momentum 5.0 and earlier | Directories in Momentum 5.1 and later | +| --- | --- | +| **/opt/msys/3rdParty/nginx** | **$NGINX_ROOT** | + +The following directories and files that were installed in **/opt/msys/3rdParty/nginx** when **msys-ecelerity-engagement-proxy** was installed are now installed in **/opt/msys/ecelerity/etc/sample-configs/nginx** _and need to be manually copied to **$NGINX_ROOT**_: + +| Installed by Momentum 5.0 and earlier | Installed by Momentum 5.1 and later | Destination of the manual copy | +| --- | --- | --- | +| **/opt/msys/3rdParty/nginx/conf.d/filter_forwarded4.lua** | **/opt/msys/ecelerity/etc/sample-configs/nginx/conf.d/filter_forwarded4.lua** | **$NGINX_ROOT/conf.d/filter_forward** | +| **/opt/msys/3rdParty/nginx/conf.d/click_proxy.conf** | **/opt/msys/ecelerity/etc/sample-configs/nginx/conf.d/click_proxy.conf** | **$NGINX_ROOT/conf.d/click_proxy.conf** | +| **/opt/msys/3rdParty/nginx/conf.d/click_proxy/base.loc** | **/opt/msys/ecelerity/etc/sample-configs/nginx/conf.d/click_proxy/base.loc** | **$NGINX_ROOT/conf.d/click_proxy/base.loc** | + +## 4.3. Node.js + +Momentum 5.3 uses Node.js 24 LTS from external providers. If you have **msys-nodejs** installed, you need to uninstall it first. The installed **msys-app-webhooks-\*** depend on **msys-nodejs** and therefore are expected to be removed as well. + +For RHEL 8 and 9, upstream **nodejs-libs** and **nodejs** RPM packages must be downloaded and installed. In RHEL 10, version 24 is available to be installed as an alternative: + +```bash +sudo dnf install -y nodejs24 +sudo alternatives --install /usr/bin/node node /usr/bin/node-24 100 +sudo alternatives --install /usr/bin/npm npm /usr/bin/npm-24 100 +sudo alternatives --install /usr/bin/npx npx /usr/bin/npx-24 100 +``` + +# 5. Download and Unpack 5.3 Webhooks + +1. Download the correct webhooks bundle for your architecture from the [Message Systems Support](https://support.messagesystems.com/start.php) website for every node you install, and unpack the file. + +2. Copy the bundle to the **/var/tmp/** directory on each node. + + ```bash + cp momentum-webhooks-bundle-5.3.x.yyyyy.rhel..tar.gz \ + /var/tmp/ + ``` + + where **\** is **8**, **9**, or **10**, and **\** is **x86_64** or **aarch64** (RHEL 9 only). + +3. Unpack the tarball on each node and set the repository directory. + + ```bash + cd /var/tmp + tar -zxf \ + momentum-webhooks-bundle-5.3.x.yyyyy.rhel..tar.gz + cd momentum-webhooks-5.3.x.yyyyy/ + ./setrepodir + ``` + + > **Note:** The **./setrepodir** script establishes some environmental parameters for the installation. If the installation is not completed within the same terminal session in which it was started, the **./setrepodir** command _must_ be re-executed in any new session(s) before executing any of the **dnf** commands for the installation. + +# 6. Install Updated Webhooks Packages + +**[MTA Nodes]** For every node you designated as an MTA (including the first node, IF it is NOT a Log Aggregator), perform the following: + +```bash +dnf install -y --config momentum.repo --enablerepo momentum \ + msys-role-webhooks +``` + +# 7. Start the Webhooks Services + +Restart the **msys-app-webhooks-api** and **msys-app-webhooks-etl** services and check the process logs for errors (use **CTRL-C** to exit the **tail -f** of the logs). + +```bash +systemctl start msys-app-webhooks-api +systemctl start msys-app-webhooks-etl +journalctl -u msys-app-webhooks-* -f +``` diff --git a/content/momentum/navigation.yml b/content/momentum/navigation.yml index bdf6aabcf..1dd09ba2f 100644 --- a/content/momentum/navigation.yml +++ b/content/momentum/navigation.yml @@ -1,3 +1,16 @@ +- link: /momentum/manuals + title: Online manuals + items: + - link: /momentum/manuals/installation-manual + title: Installation Manual + - link: /momentum/manuals/upgrade-manual + title: Upgrade Manual + - link: /momentum/manuals/enabling-apis-message-generation + title: Enabling APIs & Message Generation + - link: /momentum/manuals/enabling-webhooks + title: Enabling Webhooks + - link: /momentum/manuals/upgrading-webhooks + title: Upgrading Webhooks - link: /momentum/4 title: Momentum 4.x and later items: diff --git a/pdf-build/.gitignore b/pdf-build/.gitignore new file mode 100644 index 000000000..fcdae85ca --- /dev/null +++ b/pdf-build/.gitignore @@ -0,0 +1,8 @@ +# Generated PDFs are build artifacts, not source. Regenerate with `make`. +*.docx +build/*.pdf + +# Transient intermediate Typst files (removed after each successful build; +# may linger only if a build is interrupted). Not the template in templates/. +/*.typ +/*.typ.cols diff --git a/pdf-build/Makefile b/pdf-build/Makefile new file mode 100644 index 000000000..936ae0261 --- /dev/null +++ b/pdf-build/Makefile @@ -0,0 +1,72 @@ +# Generate branded PDF manuals from the Markdown sources that also power the +# online "Online manuals" section (content/momentum/manuals/). +# +# Single source of truth: each manual is ONE Markdown file, rendered both +# online (by the Next.js site) and as a PDF (by Pandoc + Typst, here). +# +# Requirements (not npm packages — install once per machine / CI image): +# brew install pandoc typst # macOS +# (Linux: install pandoc >= 3.x and the typst CLI) +# +# Usage (run from this directory): +# make # build a PDF for every manual +# make list # show what would be built +# make build/installation-manual.pdf # build one manual +# make clean # remove generated PDFs + +PANDOC ?= pandoc +SRC_DIR := ../content/momentum/manuals +TEMPLATE := templates/bird-manual.typ +SHARED_META := templates/metadata.yaml +BUILD_DIR := build +FONT_DIR := assets/fonts + +# Make the vendored Poppins font visible to the Typst PDF engine, so builds are +# reproducible without any system font install. +export TYPST_FONT_PATHS := $(FONT_DIR) + +# Every top-level Markdown file is a manual, except the section landing page. +SOURCES := $(filter-out $(SRC_DIR)/index.md,$(wildcard $(SRC_DIR)/*.md)) +PDFS := $(patsubst $(SRC_DIR)/%.md,$(BUILD_DIR)/%.pdf,$(SOURCES)) + +# Shared inputs every PDF depends on. Listing the logos, fonts, and the Makefile +# itself means `make` rebuilds when any of them change -- no manual `clean`. +ASSETS := $(wildcard assets/*.svg) $(wildcard $(FONT_DIR)/*.ttf) +DEPS := $(TEMPLATE) $(SHARED_META) $(ASSETS) $(MAKEFILE_LIST) + +.PHONY: all list clean +all: $(PDFS) + +# Two-step build: Pandoc -> Typst source, then compile with the typst CLI. +# The intermediate .typ is emitted here in pdf-build/ (not build/) so the +# template's relative `assets/...` logo paths resolve. It is removed after. +# +# `-tex_math_dollars`: disable $...$ math parsing so literal shell/config vars +# like $NGINX_ROOT or $PATH in prose and tables are never treated as math. +$(BUILD_DIR)/%.pdf: $(SRC_DIR)/%.md $(DEPS) | $(BUILD_DIR) + $(PANDOC) "$<" \ + --from=markdown-tex_math_dollars \ + --to=typst \ + --template=$(TEMPLATE) \ + --metadata-file=$(SHARED_META) \ + --toc --toc-depth=3 \ + --resource-path=".:$(SRC_DIR)" \ + --output="$*.typ" + @# Pandoc gives tables fixed percentage column widths (always full-width); + @# rewrite them to auto so the template can detect a table's natural width + @# and rotate genuinely wide ones onto their own landscape page. + @# Use a temp file + mv (not `sed -i`, whose syntax differs on BSD vs GNU). + sed -E '/^[[:space:]]*columns: \(/ s/[0-9.]+%/auto/g' "$*.typ" > "$*.typ.cols" && mv "$*.typ.cols" "$*.typ" + typst compile --font-path "$(FONT_DIR)" "$*.typ" "$@" + @rm -f "$*.typ" + @echo "Built $@" + +$(BUILD_DIR): + @mkdir -p $(BUILD_DIR) + +list: + @echo "Sources:"; for s in $(SOURCES); do echo " $$s"; done + @echo "PDFs:"; for p in $(PDFS); do echo " $$p"; done + +clean: + rm -f $(BUILD_DIR)/*.pdf diff --git a/pdf-build/README.md b/pdf-build/README.md new file mode 100644 index 000000000..a7c255fe7 --- /dev/null +++ b/pdf-build/README.md @@ -0,0 +1,112 @@ +# Momentum manual PDF generation + +Branded PDF editions of the on-premises Momentum manuals (installation, +upgrade, release notes, …), generated from the **same Markdown** that powers +the online [Online Manuals](../content/momentum/manuals/) section. + +``` +content/momentum/manuals/*.md ──► online pages (Next.js site) + │ + └────────────────────► PDF (Pandoc + Typst, this directory) +``` + +One manual = one Markdown file = one online page = one PDF. There is a single +source of truth; the PDF is a build artifact. + +## Toolchain + +Pandoc (≥ 3.x) and the Typst CLI. These are not npm packages — install once per +machine / CI image: + +```sh +brew install pandoc typst # macOS +# Linux: install pandoc >= 3.x and the `typst` CLI from your package manager +``` + +## Building + +```sh +cd pdf-build +make # build every manual into build/ +make list # show what would be built +make build/installation-manual.pdf # build a single manual +make clean # remove generated PDFs +``` + +Generated PDFs land in `build/` (git-ignored). + +The build is incremental: `make` rebuilds a manual when its `.md` changes, and +rebuilds **all** manuals when a shared input changes (the template, +`metadata.yaml`, a logo/font in `assets/`, or this `Makefile`). You only need +`make clean` to force a rebuild of unchanged content — e.g. after upgrading +`pandoc` or `typst`. + +## Layout + +| Path | Purpose | +| --- | --- | +| `../content/momentum/manuals/*.md` | Manual sources (also served online) | +| `templates/bird-manual.typ` | Pandoc→Typst template with the Bird-branded cover, header/footer, and styling | +| `templates/metadata.yaml` | Brand/layout defaults shared by every manual | +| `assets/` | Logo and (optional) brand fonts | +| `build/` | Generated PDFs (artifacts) | + +### Metadata + +Brand-wide settings (copyright, confidential notice, paper size, logo) live in +`templates/metadata.yaml`. Per-manual values come from each Markdown file's YAML +frontmatter and override the shared defaults: + +```yaml +--- +# Used by the website: +title: "Installation Manual" +description: "…" +lastUpdated: "06/05/2026" +# Used by the PDF cover page (ignored by the website): +subtitle: "Momentum" +version: "5.2.1" +date: "June 2026" +--- +``` + +## Adding a manual (conversion workflow) + +1. Convert the source (prefer **DOCX** over PDF — it keeps headings, lists, + tables, and images) with `pandoc`, clean it up, and save it as + `content/momentum/manuals/.md` with the frontmatter above. Put + extracted images in `content/momentum/manuals/images/` and reference them as + `images/` (so they work both online and in the PDF). + - **Numbered headings**: number headings hierarchically in the text itself + (`# 1. Introduction`, `## 2.1. …`, `### 2.3.1. …`) so the numbers show + identically online and in the PDF. + - **Code blocks**: don't reproduce the source's box colors. Use standard + fenced blocks tagged by language (` ```bash `, ` ```c `, ` ```python `, …) + for code/commands, and a plain fenced block (no language) for config-file + contents and console output — let the renderer handle highlighting. + - **Notes**: a standalone Markdown blockquote (`> **Note:** …`) becomes a + branded callout box. + - **Tables**: a table whose natural width exceeds the portrait text area is + automatically placed on its own **landscape** page; narrower tables stay + inline in portrait. For this to work a wide table must be **top-level** — + not nested inside a list item or blockquote (Typst cannot start a new page + inside a container, and the build will error). If a wide table belongs + under a step, lift it out to its own paragraph. +2. Add the page to the menu: a sub-item under the **Online manuals** node in + `content/momentum/navigation.yml`, and a link in + `content/momentum/manuals/index.md`. +3. `make build/.pdf` and review the output. + +## Branding + +- **Font**: Poppins, vendored under `assets/fonts/` (OFL) and exposed to Typst + via `TYPST_FONT_PATHS` in the `Makefile` — reproducible with no system font + install. Code spans use Typst's bundled DejaVu Sans Mono. +- **Colors** (in `templates/bird-manual.typ`): near-black `#0a0a0a` for + headings/cover, Bird blue `#0057ff` for links and sub-headings — taken from + the brand logo SVGs. +- **Cover**: full-bleed dark with the white-on-black stacked logo reversed out. + Body pages are white with the black horizontal logo in the running header. +- **Logos** in `assets/`: `bird-logo-stacked{,-dark}.svg` and + `bird-logo-horizontal{,-dark}.svg` (light = black-on-white, dark = + white-on-black). `metadata.yaml` selects which variant each placement uses. diff --git a/pdf-build/assets/README.md b/pdf-build/assets/README.md new file mode 100644 index 000000000..86a859a30 --- /dev/null +++ b/pdf-build/assets/README.md @@ -0,0 +1,25 @@ +# Brand assets for PDF generation + +## Logos + +Four Bird lockups, referenced from +[`../templates/metadata.yaml`](../templates/metadata.yaml): + +| File | Lockup | Colors | Used for | +| --- | --- | --- | --- | +| `bird-logo-stacked.svg` | stacked | black-on-white | (light cover, if used) | +| `bird-logo-stacked-dark.svg` | stacked | white-on-black | **cover page** (dark) | +| `bird-logo-horizontal.svg` | horizontal | black-on-white | official full-clear-space asset | +| `bird-logo-horizontal-header.svg` | horizontal | black, transparent | **running header** — viewBox cropped to content so it's legible when sized small | +| `bird-logo-horizontal-dark.svg` | horizontal | white-on-black | dark header, if ever used | + +SVG is used for crisp print at any size. To change which variant a placement +uses, edit `logo` / `header-logo` in `metadata.yaml`. + +## Fonts + +`fonts/` holds **Poppins** (TTF, OFL — license in `fonts/OFL.txt`), vendored so +builds are reproducible without a system font install. The `Makefile` exposes +this directory to Typst via `TYPST_FONT_PATHS`. To use a different brand cut of +Poppins (extra weights, variable font, etc.), drop the TTFs here and remove the +duplicates to avoid family clashes. diff --git a/pdf-build/assets/bird-logo-horizontal-dark.svg b/pdf-build/assets/bird-logo-horizontal-dark.svg new file mode 100644 index 000000000..24b96235d --- /dev/null +++ b/pdf-build/assets/bird-logo-horizontal-dark.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + diff --git a/pdf-build/assets/bird-logo-horizontal-header.svg b/pdf-build/assets/bird-logo-horizontal-header.svg new file mode 100644 index 000000000..71bf734f8 --- /dev/null +++ b/pdf-build/assets/bird-logo-horizontal-header.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + diff --git a/pdf-build/assets/bird-logo-horizontal.svg b/pdf-build/assets/bird-logo-horizontal.svg new file mode 100644 index 000000000..cdb66fc77 --- /dev/null +++ b/pdf-build/assets/bird-logo-horizontal.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + diff --git a/pdf-build/assets/bird-logo-stacked-dark.svg b/pdf-build/assets/bird-logo-stacked-dark.svg new file mode 100644 index 000000000..94fe2f16f --- /dev/null +++ b/pdf-build/assets/bird-logo-stacked-dark.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + diff --git a/pdf-build/assets/bird-logo-stacked.svg b/pdf-build/assets/bird-logo-stacked.svg new file mode 100644 index 000000000..e78f1528e --- /dev/null +++ b/pdf-build/assets/bird-logo-stacked.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + diff --git a/pdf-build/assets/fonts/OFL.txt b/pdf-build/assets/fonts/OFL.txt new file mode 100644 index 000000000..76df3b565 --- /dev/null +++ b/pdf-build/assets/fonts/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/pdf-build/assets/fonts/Poppins-Bold.ttf b/pdf-build/assets/fonts/Poppins-Bold.ttf new file mode 100644 index 000000000..1982f38ab Binary files /dev/null and b/pdf-build/assets/fonts/Poppins-Bold.ttf differ diff --git a/pdf-build/assets/fonts/Poppins-BoldItalic.ttf b/pdf-build/assets/fonts/Poppins-BoldItalic.ttf new file mode 100644 index 000000000..43b6b9ee3 Binary files /dev/null and b/pdf-build/assets/fonts/Poppins-BoldItalic.ttf differ diff --git a/pdf-build/assets/fonts/Poppins-Italic.ttf b/pdf-build/assets/fonts/Poppins-Italic.ttf new file mode 100644 index 000000000..863165265 Binary files /dev/null and b/pdf-build/assets/fonts/Poppins-Italic.ttf differ diff --git a/pdf-build/assets/fonts/Poppins-Medium.ttf b/pdf-build/assets/fonts/Poppins-Medium.ttf new file mode 100644 index 000000000..a590f5c3e Binary files /dev/null and b/pdf-build/assets/fonts/Poppins-Medium.ttf differ diff --git a/pdf-build/assets/fonts/Poppins-Regular.ttf b/pdf-build/assets/fonts/Poppins-Regular.ttf new file mode 100644 index 000000000..0bda228ad Binary files /dev/null and b/pdf-build/assets/fonts/Poppins-Regular.ttf differ diff --git a/pdf-build/assets/fonts/Poppins-SemiBold.ttf b/pdf-build/assets/fonts/Poppins-SemiBold.ttf new file mode 100644 index 000000000..c30ad1047 Binary files /dev/null and b/pdf-build/assets/fonts/Poppins-SemiBold.ttf differ diff --git a/pdf-build/templates/bird-manual.typ b/pdf-build/templates/bird-manual.typ new file mode 100644 index 000000000..1747b892f --- /dev/null +++ b/pdf-build/templates/bird-manual.typ @@ -0,0 +1,354 @@ +// ============================================================================ +// Bird / Momentum branded Pandoc -> Typst template. +// +// This is Pandoc's default Typst template (pandoc -D typst) with ONE change: +// the built-in `conf` import is replaced by the inline, Bird-branded `conf` +// defined below. Keeping the surrounding template byte-for-byte identical to +// Pandoc's default means all of Pandoc's body-rendering machinery (tables, +// term lists, syntax highlighting, figures, bibliography) keeps working across +// Pandoc upgrades -- only the cosmetic `conf` is ours. +// +// To re-sync after a Pandoc upgrade: run `pandoc -D typst`, diff against this +// file, and re-apply the two marked edits (the `#let conf` block, and the +// extra title-page arguments in the `#show: doc => conf(...)` call). +// +// Brand tokens (colors, fonts, logo) live at the top of `conf` and are marked +// `TODO`. Confirm the official Bird values before the first customer release. +// ============================================================================ + +#let horizontalrule = line(start: (25%,0%), end: (75%,0%)) + +#show terms.item: it => block(breakable: false)[ + #text(weight: "bold")[#it.term] + #block(inset: (left: 1.5em, top: -0.4em))[#it.description] +] + +#set table( + inset: 6pt, + stroke: none +) + +#show figure.where( + kind: table +): set figure.caption(position: $if(table-caption-position)$$table-caption-position$$else$top$endif$) + +#show figure.where( + kind: image +): set figure.caption(position: $if(figure-caption-position)$$figure-caption-position$$else$bottom$endif$) + +$if(highlighting-definitions)$ +// syntax highlighting functions from skylighting: +$highlighting-definitions$ + +$endif$ +// ---- BIRD EDIT 1/2: inline branded `conf` (replaces pandoc's import) -------- +#let conf( + title: none, + subtitle: none, + version: none, + authors: (), + keywords: (), + date: none, + copyright: none, + confidential: none, + logo: none, + header-logo: none, + lang: "en", + region: "US", + abstract-title: none, + abstract: none, + thanks: none, + margin: (x: 2.5cm, y: 2.5cm), + paper: "a4", + font: ("Poppins",), + fontsize: 10pt, + mathfont: none, + codefont: ("DejaVu Sans Mono",), + linestretch: 1.2, + sectionnumbering: none, + pagenumbering: "1", + linkcolor: none, + citecolor: none, + filecolor: none, + cols: 1, + doc, +) = { + // ---- Brand tokens (from the Bird logo SVGs) ------------------------------ + let brand-color = rgb("#0a0a0a") // near-black: headings / titles + let accent-color = rgb("#0057ff") // Bird blue: links / sub-headings + let dark-color = rgb("#0a0a0a") // cover background (matches dark logo bg) + + set document( + title: if title != none { title } else { "" }, + author: authors.map(a => if type(a) == dictionary { a.name } else { a }), + ) + set text(font: font, size: fontsize, lang: lang, region: region) + set par(leading: 0.65em * linestretch, justify: true) + set heading(numbering: sectionnumbering) + + // Heading styles. Each level-1 (main) section starts on a new page; the + // weak break is skipped when the page is already empty, so no blank pages. + show heading.where(level: 1): it => { + pagebreak(weak: true) + block(below: 0.8em)[ + #set text(fill: brand-color, weight: "bold", size: 1.5em) + #it + ] + } + show heading.where(level: 2): set text(fill: accent-color, weight: "bold") + + // Links + monospace code + show link: set text(fill: accent-color) + show raw: set text(font: codefont) + show raw.where(block: true): it => block( + fill: luma(245), inset: 8pt, radius: 3pt, width: 100%, breakable: true, + )[#it] + + // Notes/callouts: Markdown blockquotes become a branded box -- light blue + // tint, brand-blue left accent bar, blue label (the leading **Note:**), and + // italic body to set them apart from the running text. + show quote.where(block: true): it => block( + width: 100%, + fill: rgb("#eef3ff"), + inset: (x: 12pt, y: 9pt), + radius: 3pt, + stroke: (left: 3pt + accent-color), + )[ + #set text(style: "italic") + #show strong: set text(fill: accent-color, style: "normal") + #it.body + ] + + // Let tables (which Pandoc wraps in a figure) break across pages instead of + // overflowing when they are taller than one page. + show figure: set block(breakable: true) + + // Wide tables: if a table's natural (unwrapped) width exceeds the portrait + // text area, place it on its own landscape page; content flows back to + // portrait afterwards. Narrower tables stay inline. + let margin-x = if type(margin) == dictionary and "x" in margin { margin.x } else { 2.5cm } + let portrait-text-width = 21cm - 2 * margin-x // assumes A4 width (21cm) + show figure.where(kind: table): it => context { + if measure(it).width > portrait-text-width { + page(flipped: true, it) + } else { + it + } + } + + // ---- Cover page: full-bleed dark, reversed-out logo ---------------------- + // Provide the dark (white-on-black) stacked lockup as `logo` so it blends + // into the dark background. + page( + paper: paper, + margin: margin, + fill: dark-color, + header: none, footer: none, numbering: none, + )[ + #set align(center) + #set text(fill: white) + #v(1fr) + #if logo != none [ #image(logo, width: 6cm) #v(1.5cm) ] + #if subtitle != none [ + // Product name is the subject of the manual -> headline. + #text(size: 40pt, weight: "bold", tracking: 0.5pt)[#subtitle] + #v(0.45cm) + #text(size: 19pt, fill: accent-color.lighten(35%))[#title] + ] else [ + #text(size: 30pt, weight: "bold")[#title] + ] + #if version != none [ #v(0.35cm) #text(size: 13pt, fill: luma(210))[Version #version] ] + #v(1fr) + #if date != none [ #text(size: 11pt, fill: luma(210))[#date] #linebreak() ] + #if confidential != none [ #text(size: 9pt, fill: luma(160))[#confidential] #linebreak() ] + #if copyright != none [ #text(size: 9pt, fill: luma(160))[#copyright] ] + ] + + // ---- Body pages: running header + footer, page numbers reset to 1 -------- + // Header carries the small horizontal Bird lockup (if provided), else text. + set page( + paper: paper, + margin: margin, + numbering: pagenumbering, + header: context { + set text(size: 8pt, fill: luma(130)) + let info = { + if title != none [ #title ] + if title != none and version != none [ #h(0.5em)·#h(0.5em) ] + if version != none [ #version ] + } + // Logo (left) and title·version (right) share one row, bottom-aligned. + if header-logo != none { + grid( + columns: (auto, 1fr), + align: (left + bottom, right + bottom), + image(header-logo, height: 1.8em), + info, + ) + } else { + align(right, info) + } + v(0.15em) + line(length: 100%, stroke: 0.5pt + luma(210)) + }, + footer: context { + set text(size: 8pt, fill: luma(130)) + if confidential != none [ #confidential ] + h(1fr) + counter(page).display(pagenumbering) + }, + ) + counter(page).update(1) + + if abstract != none { + block(inset: (x: 1.5em), above: 1em, below: 1.5em)[ + #if abstract-title != none [ #text(weight: "bold")[#abstract-title] #linebreak() ] + #abstract + ] + } + + set par(first-line-indent: 0pt) + if cols > 1 { columns(cols, doc) } else { doc } +} +// ---- end BIRD EDIT 1/2 ------------------------------------------------------ + +$if(smart)$ +$else$ +#set smartquote(enabled: false) + +$endif$ +$for(header-includes)$ +$header-includes$ + +$endfor$ +#show: doc => conf( +$if(title)$ + title: [$title$], +$endif$ +$if(subtitle)$ + subtitle: [$subtitle$], +$endif$ +$-- ---- BIRD EDIT 2/2: extra title-page arguments ---- +$if(version)$ + version: [$version$], +$endif$ +$if(copyright)$ + copyright: [$copyright$], +$endif$ +$if(confidential)$ + confidential: [$confidential$], +$endif$ +$if(logo)$ + logo: "$logo$", +$endif$ +$if(header-logo)$ + header-logo: "$header-logo$", +$endif$ +$-- ---- end BIRD EDIT 2/2 ---- +$if(author)$ + authors: ( +$for(author)$ +$if(author.name)$ + ( name: [$author.name$], + affiliation: [$author.affiliation$], + email: [$author.email$] ), +$else$ + ( name: [$author$], + affiliation: "", + email: "" ), +$endif$ +$endfor$ + ), +$endif$ +$if(keywords)$ + keywords: ($for(keywords)$$keywords$$sep$,$endfor$), +$endif$ +$if(date)$ + date: [$date$], +$endif$ +$if(lang)$ + lang: "$lang$", +$endif$ +$if(region)$ + region: "$region$", +$endif$ +$if(abstract-title)$ + abstract-title: [$abstract-title$], +$endif$ +$if(abstract)$ + abstract: [$abstract$], +$endif$ +$if(thanks)$ + thanks: [$thanks$], +$endif$ +$if(margin)$ + margin: ($for(margin/pairs)$$margin.key$: $margin.value$,$endfor$), +$endif$ +$if(papersize)$ + paper: "$papersize$", +$endif$ +$if(mainfont)$ + font: ("$mainfont$",), +$endif$ +$if(fontsize)$ + fontsize: $fontsize$, +$endif$ +$if(mathfont)$ + mathfont: ($for(mathfont)$"$mathfont$",$endfor$), +$endif$ +$if(codefont)$ + codefont: ($for(codefont)$"$codefont$",$endfor$), +$endif$ +$if(linestretch)$ + linestretch: $linestretch$, +$endif$ +$if(section-numbering)$ + sectionnumbering: "$section-numbering$", +$endif$ + pagenumbering: $if(page-numbering)$"$page-numbering$"$else$"1"$endif$, +$if(linkcolor)$ + linkcolor: [$linkcolor$], +$endif$ +$if(citecolor)$ + citecolor: [$citecolor$], +$endif$ +$if(filecolor)$ + filecolor: [$filecolor$], +$endif$ + cols: $if(columns)$$columns$$else$1$endif$, + doc, +) + +$for(include-before)$ +$include-before$ + +$endfor$ +$if(toc)$ +#outline( + title: $if(toc-title)$[$toc-title$]$else$auto$endif$, + depth: $toc-depth$ +); +$endif$ + +$body$ + +$if(citations)$ +$for(nocite-ids)$ +#cite(label("${it}"), form: none) +$endfor$ +$if(csl)$ + +#set bibliography(style: "$csl$") +$elseif(bibliographystyle)$ + +#set bibliography(style: "$bibliographystyle$") +$endif$ +$if(bibliography)$ + +#bibliography(($for(bibliography)$"$bibliography$"$sep$,$endfor$)$if(full-bibliography)$, full: true$endif$) +$endif$ +$endif$ +$for(include-after)$ + +$include-after$ +$endfor$ diff --git a/pdf-build/templates/metadata.yaml b/pdf-build/templates/metadata.yaml new file mode 100644 index 000000000..6f7a6cd66 --- /dev/null +++ b/pdf-build/templates/metadata.yaml @@ -0,0 +1,30 @@ +# Shared brand/layout defaults applied to every generated Momentum manual PDF. +# +# Per-manual values (title, subtitle, version, date) come from each Markdown +# file's own YAML frontmatter and OVERRIDE anything set here. Keep brand-wide, +# manual-independent settings in this file. + +papersize: a4 +fontsize: 10pt +linestretch: 1.2 +toc-title: "Table of Contents" + +# Body/heading font. Poppins is vendored in assets/fonts/ and made available to +# Typst via TYPST_FONT_PATHS in the Makefile, so builds are reproducible without +# any system font install. (Code spans use Typst's bundled DejaVu Sans Mono.) +mainfont: "Poppins" + +margin: + x: 2.5cm + y: 2.5cm + +# --- Bird brand ------------------------------------------------------------- +copyright: "© 2014–2026 Bird B.V. All rights reserved." +confidential: "CONFIDENTIAL - FOR BIRD CUSTOMERS ONLY - NOT FOR DISTRIBUTION" + +# Bird logos. Paths are resolved relative to the pdf-build/ directory. +# The cover page is dark, so `logo` uses the white-on-black (dark) stacked +# lockup; the running header is on white pages, so it uses the black (light) +# horizontal lockup. Light/dark counterparts of each also live in assets/. +logo: "assets/bird-logo-stacked-dark.svg" +header-logo: "assets/bird-logo-horizontal-header.svg"