Engage Rallypoints - rallytac/pub GitHub Wiki

Engage uses the multicast capabilities inherent in your network to provide a transport mechanism between Engines (and therefore the users of those Engines). However, what if your network doesn't support multicast or—as is often the case—you need to communicate over someone else's network, such as the Internet?

That's where Engage Rallypoints come in.

Engage's Rallypoints are small, super-fast packet routers designed to securely forward packets between Engage Engines that are unable to communicate with each other over multicast. So when Engage users need to speak with each other over something like the Internet, Rallypoints provide the means to do so.

Prerequisites

Operating System

Rallypoints run on Linux, macOS, and Microsoft Windows—with Linux being the preferred platform. For Linux, use a current Red Hat–family distribution (RHEL, Rocky Linux, AlmaLinux, or similar) or a Debian-family distribution (Ubuntu LTS or Debian stable). Older releases such as CentOS 7 or Ubuntu 18.04 may still run packaged builds, but newer LTS/stable releases are recommended.

IP Ports

Client and peer Rallypoint links use a reliable TLS control path. The default is TLS over TCP. Optional alternatives are WebSocket over TLS and QUIC (TLS 1.3 over UDP). Media can stay on that same reliable path or move to UDP when udpStreaming is configured; see Rallypoint Link Options.

TCP

The default inbound TCP port is 7443 and uses TLS v1.3. Make sure this port is open for inbound connections from Engage clients and other Rallypoints through your firewalls and other network infrastructure. (We mention TLS so that if your environment performs deep packet inspection, TLS passthrough, or similar checks for DoS detection, you can configure it accordingly.)

If you enable the optional WebSocket listener, also open its TCP port (default 8443) with the TLS/client-cert policy you configure there.

If you enable the optional QUIC listener, also open UDP port 7443 (or quic.listenPort). That is the same number as the TCP listen port; it is a different IP protocol. openssl s_client without -quic will not speak this path — on OpenSSL 3.5 use openssl s_client -quic -alpn rts-rallypoint-v1.

Also, for environments where load balancers or other network infrastructure check availability by opening TCP connections, the Rallypoint may be configured to listen for inbound connections on a separate health-check port. If you operate in such an environment, open the port you configure for that purpose. This health-check port typically has little or no application traffic—most health checkers simply open the connection and either close it right away or keep it open briefly. If you enable this, DoS detection logic in firewalls and/or your operating system may need tuning to tolerate fast connect/disconnect cycles from the health checker.

UDP

Rallypoints also use UDP in two related ways:

  1. Multicast reflection / forwarding — forwarding traffic between unicast TLS paths and multicast UDP. This can create a multicast backbone between Rallypoints, or bridge multicasting Engage Engines / third-party gateways to unicast Engage clients. If you forward multicast over unicast (and vice versa), configure the host firewall for multicast RX/TX and open the necessary UDP ports.
  2. Optional UDP media streaming on client/peer links — when udpStreaming is enabled, media can ride UDP (default listen port 7444) while control stays on TLS/TCP, WebSocket, or QUIC. See Rallypoint Link Options and the udpStreaming configuration section below.
  3. Optional QUIC control — when quic.enabled is true, Engage clients and RP peers can use UDP 7443 (configurable) for the same length-prefixed RP protocol on one reliable QUIC stream. This is not a replacement for udpStreaming.

Installation

Prepackaged

A Rallypoint is most easily installed with the package manager for your operating system using the installation package provided by Rally Tactical Systems. These packages install the binaries, factory-default certificates, and a baseline configuration. They also set up the Rallypoint as a daemon (background service) that starts at boot—via systemd on Linux and launchd on macOS.

For Red Hat–family distributions:

sudo yum install <rallypoint_package_file>.rpm

For Debian-based distributions:

sudo apt install <rallypoint_package_file>.deb

NOTE: In the examples above we're telling yum or apt to install from a file. To ensure the tools use the file and not a named package from a repository, change to the directory that contains the file and precede the file name with ./. For example:

sudo yum install ./rallypointd-1.189.9026-0.x86_64.rpm

For macOS, open the <rallypoint_package_file>.dmg and double-click the install icon.

Manual Installation

If you need a more sophisticated install, want to run the Rallypoint process manually (not as a background daemon), or generally have more complex needs, install the relevant pieces by hand. It's straightforward.

Assume for now that you're not (yet) configuring a background service:

  • Place the rallypointd executable anywhere you'd like—a custom directory or a standard location such as /usr/sbin. As long as it can be executed from that path, you're good.
  • Place the security-related certificate and key files where the Rallypoint can read them: the Rallypoint's certificate and that certificate's private key. Restrict that location to the Rallypoint and other authorized applications/users. Also place CA certificates used to verify client and peer Rallypoint certificates where the Rallypoint can read them.
  • Place your configuration file where the Rallypoint can read it. By default it looks for /etc/rallypointd/rallypointd_conf.json.

Note: If you put the configuration file elsewhere or give it a different name, tell the Rallypoint with the -cfg command-line parameter. For example:

rallypointd -cfg:my_custom_configuration.json

Manual Daemon Configuration

Once everything is installed manually, you may still want the Rallypoint to run as a daemon at startup and use OS service management. On Linux, set up systemd (or, on older systems, init-style scripts). Refer to your operating system's documentation.

Setting up a launchd job on macOS is a bit more involved. Apple's documentation is a good starting point:

https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html

Operation

Once the software is installed and configured (more on that below), your Rallypoint should start and begin accepting connections from clients and/or other Rallypoint peers. If it runs as a daemon under systemd, use the usual systemctl / journalctl commands:

Operation Command Line
Starting the service sudo systemctl start rallypointd
Stopping the service sudo systemctl stop rallypointd
Restarting the service sudo systemctl restart rallypointd
Query service status sudo systemctl status rallypointd
Watch the log sudo journalctl -f -u rallypointd

If you run rallypointd from the command line, log output appears in the terminal. Stop the process with Ctrl-C or kill.

Monitoring

You can monitor the Rallypoint in several ways.

Logging

The simplest is viewing the log in a terminal—directly from the process or, if running as a daemon, with journalctl as above.

Log output is also sent to the OS logging subsystem: syslog on Linux and Apple's unified logging on macOS.

Messages follow the syslog style (timestamp and severity). You can feed them to log-processing tools such as SolarWinds, Papertrail, and similar systems for alerts to operators or automation.

Note: In a terminal with ANSI color support, log lines are colorized to make important messages easier to spot.

Status Report

In addition to the log, the Rallypoint can produce a periodic status report. The report is JSON, written to a file you configure, at an interval you choose (we recommend 30-second intervals, with 5 seconds as a practical minimum). Analyze that file to judge Rallypoint health.

Here's an illustrative example (comments are for explanation only—they are not valid in a real JSON file):

{
   "id":"demorp0001",            // The Rallypoint's instance identifier
   "ts":119790397,               // UTC UNIX timestamp (number of seconds 
                                 // since Jan 1, 1970) of when this report 
                                 // was produced
   "uptime": 149663,             // Number of seconds the process has been
                                 // up
   "systemCpuLoad":4.86,         // Percentage CPU load of the machine instance 
                                 // hosting the Rallypoint

   "connections":
   {      
      "active":1,                // Number of active client connections
      "denied":0,                // Number of connection requests denied
      "total":3                  // Total process lifetime count of client 
                                 // connections
   },

   "healthChecks":
   {
      "count":0,                 // Number of TCP health checks made by a 
                                 // load-balancer or other network 
                                 // infrastructure entity
      "rate":0.0,                // Health checks per second 
      "rateEma":0.0              // Exponential moving average of health 
                                 // checks per second 
   },

   "peers":
   {
      "configuredConnectedCount":0,    // Number of configured peer 
                                       // connections that are connected
      "configuredCount":0,             // Number of configured peer connections
      "count":0,                       // Number of connected Rallypoint 
                                       // peers (inbound and outbound)
      "leafConnectedCount":0,          // Number of peers that are inbound leaf
                                       // peer nodes
      "list":[]                        // List of peers
   },

   "queue":
   {
      "avgExecNanos":60567,               // Average number of nanoseconds a queue 
                                          // operation takes to execute
      "maxExecNanos":7733634,             // Longest number of nanoseconds a queue
                                          // operation took to execute
      "minExecNanos":0,                   // Least number of nanoseconds a queue
                                          // operation took to execute
      "lowPriorityQueueDepth":0,          // Current number of operations waiting in the 
                                          // low priority queue
      "lowPriorityQueueMaxDepth":0,       // Maximum number of operations in the low-
                                          // priority queue
      "lowPriorityQueueFailures":0,       // Number of operations denied entrance to
                                          // the low-priority queue due to load
      "normalPriorityQueueDepth":0,       // Current number of operations waiting in the 
                                          // normal-priority queue
      "normalPriorityQueueMaxDepth":1,    // Maximum number of operations in the normal-
                                          // priority queue
      "normalPriorityQueueFailures":0,    // Number of operations denied entrance to
                                          // the normal-priority queue due to load

      "ops":
      {
         "count":3360,           // Total number of process 
                                 // lifetime operations
         "rate":145.0,           // Operations per second
         "rateEma":15.002        // Exponential moving average 
                                 // of operations per second
      },
      "spuriousWakeups":114,     // Number of queue wakeups that
                                 // resulted in a no-op
      "wakeUps":3346             // Total number of queue wakeups
   },

   "routing":
   {
      "blobs":
      {
         "rx":
         {
            "bytes":293895,      // Number of received blob bytes
            "packets":3118       // Number of received blob packets
         },

         "tx":
         {
            "bytes":292495,      // Number of transmitted blob bytes
            "packets":3116       // Number of transmitted blob packets
         }
      },
      "paths":28,                // Number of potential uni-directional 
                                 // media stream pathways defined by the 
                                 // routing table
      "streams":5                // Number of registered streams
   },

   "rx":
   {
      "bytes":303209,            // Number of received bytes
      "packets":3133,            // Number of received packets
      "rate":106643.2,           // Received bytes per second
      "rateEma":9613.413         // Exponential moving average of
                                 // received bytes per second
   },

   "tx":
   {
      "bytes":297647,            // Number of transmitted bytes
      "packets":3131,            // Number of transmitted packets
      "rate":106643.2,           // Transmitted bytes per second
      "rateEma":9397.57          // Exponential moving average of
                                 // transmitted bytes per second
   },

   "throughput":
   {
      "rate":0,                  // Active network I/O throughput
                                 // in bits per second
      "rateEma":0                // Exponential moving average of
                                 // network I/O throughput (bps)
   }
}

The most important performance signal for user experience is queue/normalPriorityQueueDepth. Values greater than zero for a prolonged period mean the Rallypoint is falling behind on packet processing—degraded audio and higher latency. Causes include CPU pressure, memory pressure, or I/O backup. Scale up the host/VM or add Rallypoints if you run a meshed cloud.

Also useful: queue/ops/rate—operations per second (routing a packet, handling a client request, and so on). On a reasonably powerful server-class machine, a Rallypoint can comfortably process well over 500,000 operations per second through its queue. An example rate of 145.0/sec means that particular Rallypoint is basically idle.

Configuration

Now that you've seen how to install, operate, and monitor the software, it's time to configure it.

We want to take a moment to stress something.

It is vitally important that you safeguard access to your X.509 certificate files and, in particular, private keys. While certificates are ultimately public, private keys are just that—PRIVATE. Make sure that only your Rallypoint and other authorized users and entities have access to these files.

As you've probably guessed by now, a Rallypoint is configured with a JSON file at /etc/rallypointd/rallypointd_conf.json, or one you specify on the command line with -cfg.

The shipped rallypointd_conf.json is the authoritative full schema. Sections below document every operator-facing object. For TCP vs UDP media on client links, see Rallypoint Link Options. Where a setting is parsed but not yet fully wired in the current rallypointd build, that is called out explicitly.

Here's an illustrative example (again, inline // comments are for explanation only):

{
   "id":"rp0001",                         
   "listenPort":7443,
   "interfaceName":"en0",
   "multicastInterfaceName":"en0",
   "allowMulticastForwarding":false,
   "ioPools":-1,
   "allowPeerForwarding":false,
   "isMeshLeaf":false,
   
   "certStoreFileName":"/etc/rallypointd/rallypointd.certstore",
   "certStorePasswordHex":"",

   "peeringConfigurationFileName": "",
   "peeringConfigurationFileCommand":"",
   "peeringConfigurationFileCheckSecs":30,

   "limits":
   {
      "maxClients":0,
      "maxPeers":0,
      "maxMulticastReflectors":0,
      "maxRegisteredStreams":0,
      "maxStreamPaths":0,
      "maxRxPacketsPerSec":0,
      "maxTxPacketsPerSec":0,
      "maxRxBytesPerSec":0,
      "maxTxBytesPerSec":0,
      "maxQOpsPerSec":0
   },

   "statusReport":
   {
      "enabled":true,
      "fileName":"/tmp/${id}_status.json",
      "intervalSecs":30,
      "includeLinks":true,
      "includePeerLinkDetails":true,
      "includeClientLinkDetails":false
   },
   
   "linkGraph":
   {
      "enabled":true,
      "fileName":"/tmp/${id}_links.dot",
      "minRefreshSecs":5,
      "includeDigraphEnclosure":true,
      "includeClients":false,
      "coreRpStyling":"[shape=hexagon color=firebrick style=filled]",
      "leafRpStyling":"[shape=box color=gray style=filled]",
      "clientStyling":"[dir=none]"
   },        

   "externalHealthCheckResponder":
   {
      "listenPort":0,
      "immediateClose":true
   },
        
   "certificate":
   {
      "certificate":"@certstore://rtsFactoryDefaultRpSrv",
      "key":"@certstore://rtsFactoryDefaultRpSrv"
   },

   "tls":
   {
      "verifyPeers":true,
      "allowSelfSignedCertificates":false,
      "caCertificates":
      [
         "@certstore://rtsCA"
      ],
      "crlSerials":
      [
         "ad:de:61:33:99:67:21:e1",
         "6B:D6:13:51:42:F5:04:31"
      ]
   },

   "fipsCrypto":{
      "enabled":false,
      "path":"/etc/rallypointd",
      "curves":"secp521r1",
      "ciphers":"TLS_AES_256_GCM_SHA384"
   },

   "tcpTxOptions":
   {
      "priority":4,
      "ttl":128
   },

   "multicastTxOptions":
   {
      "priority":4,
      "ttl":1
   },

   "udpStreaming":
   {
      "enabled":false,
      "cryptoType":1,
      "listenPort":7444,
      "keepaliveIntervalSecs":15,
      "priority":3,
      "ttl":64,
      "ipv4":
      {
         "enabled":true,
         "external":
         {
            "address":"",
            "port":0
         }
      },
      "ipv6":
      {
         "enabled":true,
         "external":
         {
            "address":"",
            "port":0
         }
      }
   },

   "multicastRestrictions": 
   {
      "type":1,
      "elements":
      [
         {
            "rx": 
            {
               "address":"234.1.2.3",
               "port":25000
            },
            "tx": 
            {
               "address":"234.1.2.3",
               "port":25000
            }
         },

         {
            "rx": 
            {
               "address":"234.5.6.7",
               "port":17222
            },
            "tx": 
            {
               "address":"234.5.6.7",
               "port":17222
            }
         }
      ]
   },

   "groupRestrictionAccessPolicyType": 0,

   "groupRestrictions":
   {
      "type":1,
      "elements":
      [
         "{58e3e468-c0e1-4ad8-86d2-9931251e6ea0}",
         "{b7694a4f-9724-44a6-ae57-c63232ad1f57}"
      ]
   },

   "extendedGroupRestrictions": [
      {
         "id": "{58e3e468-c0e1-4ad8-86d2-9931251e6ea0}",
         "restrictions": [
            {
               "type": 1,
               "elementsType": 2,
               "elements": [
                  "-educators",
                  "-teachers",
                  "-instructors"
               ]
            },
            {
               "type": 2,
               "elementsType": 5,
               "elements": [
                  "ST=WA",
                  "ST=ID"
               ]
            }
         ]
      },
      {
         "id": "{b7694a4f-9724-44a6-ae57-c63232ad1f57}",
         "restrictions": [
            {
               "type": 1,
               "elementsType": 6,
               "elements": [
                  "O=My Fictional Issuing Organization"
               ]
            }
         ]
      }
   ],
      
	"staticReflectors": 
   [
      {
         "id":"{58e3e468-c0e1-4ad8-86d2-9931251e6ea0}",
         "rx":
         {
            "address":"234.1.2.3",
            "port":25000
         },
         "tx":
         {
            "address":"234.1.2.3",
            "port":25000
         }
      },
      {
         "id":"{b7694a4f-9724-44a6-ae57-c63232ad1f57}",
         "rx":
         {
            "address":"234.5.6.7",
            "port":17222
         },
         "tx":
         {
            "address":"234.5.6.7",
            "port":17222
         }
      }
   ]
}

Let's go through these in detail. Field names match the shipped JSON schema (tcpTxOptions, not the older wiki name txOptions).

root

The root of the configuration has a number of elements that are key to Rallypoint operation.

  • id is a unique string you assign that identifies this Rallypoint instance. It is very important that this string be unique because it has meaning when multiple Rallypoints are meshed. If you leave this element blank, the Rallypoint generates a value automatically—but that works poorly for meshing, so we recommend you always set it. The computer's host name works well, or in Docker/cloud environments, the ID assigned by the container or cloud system.

  • name — optional human-readable display name (separate from id).

  • listenPort is the TCP port the Rallypoint listens on for TLS connections from Engage clients and other Rallypoints. As described earlier, this port runs TLS, so firewalls must allow inbound TLS to this port. The default is 7443; you can assign any valid TCP port.

  • ipFamily — address family for the listen socket: 4 = IPv4 (default), 6 = IPv6. Set explicitly; do not leave an unspecified 0 in production configs.

  • interfaceName is the name of the OS network interface used for listenPort. If you don't assign a name, the Rallypoint binds listenPort on all NICs.

  • multicastInterfaceName is the NIC used for receiving and sending multicast UDP. It can match interfaceName or differ if you forward onto a backbone via another interface. For security reasons you must set the multicast interface name. If you leave it blank, multicast forwarding is disabled. Discovery protocols that need a multicast NIC also fall back to this name.

  • allowMulticastForwarding indicates whether forwarding traffic to multicast is allowed. The default is false. If enabled, endpoints that register streams with multicast addressing cause the Rallypoint to forward automatically—no other local reflector configuration is required for dynamic cases. When forwarding unicast TLS traffic to multicast, the TLS security envelope (the "TRANSEC") is removed and only the TLS payload contents are forwarded. Ensure Engage groups use encryption (the "COMSEC") unless you deliberately need clear traffic for third-party entities (such as LMR gateways) that do not support encryption.

  • forwardDiscoveredGroups — when true, intended to auto-create multicast reflectors for groups learned via discovery. Default false. Current builds still treat much of this path as incomplete—do not rely on it alone; prefer staticReflectors or client-learned reflection.

  • forwardMulticastAddressing — when true, peer subscriptions include multicast RX/TX addresses so peers can reflect that traffic. Default false. Widen topology carefully; combine with multicastRestrictions and leaf policy.

  • ioPools tells the Rallypoint how many threads of parallel operation to set up for network I/O. If you leave this blank or set it to -1, the Rallypoint sets up one I/O pool per CPU. Leave this at -1 unless you have a specific reason to change it.

  • allowPeerForwarding indicates whether unicast traffic received from Rallypoint peers in a mesh should be forwarded to other Rallypoints. This is an experimental feature and should stay disabled unless you fully understand implications such as packet loops.

  • isMeshLeaf indicates whether the Rallypoint is a "leaf" hanging off a mesh or part of the core mesh. More in Connecting Into A Mesh.

  • enableLeafReflectionReverseSubscription — when documented as enabled, a leaf would reverse-subscribe into the core when reflection is set up. Default false. Not currently wired in rallypointd runtime—leave false.

  • disableLoopDetection — turns off peer loop-detection messaging/checks. Default false. Disabling can create routing loops on a mesh. Leave false unless RTS directs otherwise.

  • disableMessageSigning — skip DSA signing on registration/control messages. Default false. Security risk—only consider on CPU-constrained hosts on already-locked networks.

  • maxSecurityLevel — maximum stream security classification the RP will accept (0 = only level 0, the default). Higher client levels are denied.

  • streamIdPrivacyType — how stream/group IDs are transformed for routing privacy when talking to peers:

    • 0 — use the stream ID as-is (default)
    • 1–18 — derive a privacy-transformed ID from peer certificate material (certificate, public key, subject/issuer DN fragments, fingerprint, serial, and so on). Changing this alters effective IDs used for routing and restrictions—coordinate across the mesh before enabling.
  • domainName — this RP’s logical domain. Empty means domain checks are off.

  • allowedDomains — domains that may connect when domain enforcement is on (own domainName is included automatically). Empty list with a set domainName ⇒ only the local domain.

  • blockedDomains — domains that must not connect (evaluated before allow).

  • extraDomains — additional domains reachable via this RP; advertised in peer handshake and mDNS TXT when advertising is enabled.

  • sysFlags — internal bit flags. Known: 0x0001 ignores featureset/licensing gates for some capabilities. Default 0. Using the bypass in production is an operational risk.

  • normalTaskQueueBias — low-level TaskExecutor bias for the normal-priority queue. Default 0. Leave alone unless directed.

  • configurationCheckSignalName — POSIX semaphore name; signaling it triggers a peering-configuration re-check. Default rts.7b392d1.${id} (${id} expanded). Empty disables signalled reload.

  • maxOutboundPeerConnectionIntervalDeltaSecs — cap on outbound peer reconnect backoff growth (seconds). Default 15.

  • peerRtTestIntervalMs — interval for peer round-trip (RTT) probes. Default 60000. Set <= 0 to disable probes.

  • peerRtBehaviors — array of RTT threshold behaviors:

    • atOrAboveMs — RTT threshold
    • behavior — btReportInfo, btReportWarn, btReportError, or btDrop (99, closes the peer)
    • runCmd — optional shell command; may substitute ${ms} and ${peer} Misconfigured btDrop / runCmd can disconnect peers or run arbitrary commands as the RP user.
  • certStoreFileName is the path to the certificate store. See Engage Security.

  • certStorePasswordHex is the hex representation of the password/passphrase protecting the certificate store. See Engage Security.

  • peeringConfigurationFileName is the file from which the Rallypoint loads mesh peer details. If there is no mesh, leave this blank. (More later.)

  • peeringConfigurationFileCommand is an OS command to run instead of polling the file named by peeringConfigurationFileName. The command must write JSON (in the mesh configuration format) to STDOUT and finish quickly (under 30 seconds) or the Rallypoint will try to terminate it.

IMPORTANT: peeringConfigurationFileName and peeringConfigurationFileCommand are mutually exclusive. If both are set, the Rallypoint fails to configure and aborts.

  • peeringConfigurationFileCheckSecs is the interval (seconds) at which the Rallypoint checks peeringConfigurationFileName for changes or, if peeringConfigurationFileCommand is set, runs that command. Default in code is 60 (sample configs often use 30).

limits

Operational boundaries and load shedding. For the classic max* caps, 0 means unlimited / disabled.

  • maxClients / maxPeers / maxMulticastReflectors / maxRegisteredStreams / maxStreamPaths
  • maxRxPacketsPerSec / maxTxPacketsPerSec / maxRxBytesPerSec / maxTxBytesPerSec / maxQOpsPerSec
  • maxInboundBacklog — listen backlog (default 64; 0 is forced to 64 at start)
  • lowPriorityQueueThreshold — deny new connections when low-priority queue depth is at or above this (default 64)
  • normalPriorityQueueThreshold — same for the normal-priority queue (default 256)
  • denyNewConnectionCpuThreshold — CPU % (0–100) at which new connections are denied (default 75)
  • warnAtCpuThreshold — CPU % at which warnings are logged (default 65)

Set queue/CPU thresholds too low and healthy load gets refused; too high and the RP overloads before shedding.

watchdog

Monitors the task executor for hangs.

  • enabled — default true
  • intervalMs — how often to check (default 5000)
  • hangDetectionMs — stall duration that counts as a hang (default 2000)
  • abortOnHang — if true (default), abort the process on hang (typically disabled while a debugger is attached / in debug builds)
  • slowExecutionThresholdMs — log slow task execution above this (default 100)

statusReport

Controls the status report described earlier.

  • enabled — set true to enable (default false).
  • fileName — full path of the JSON status file. Blank means no report. Include ${id} to insert the Rallypoint ID into the name.
  • intervalSecs — how often to write the report. You can set this as low as 1 second; we recommend no lower than 5 seconds. 30 seconds is generally reasonable (code default when unset is 60).
  • includeLinks — include link information (default false).
  • includePeerLinkDetails — include peer link details when includeLinks is true (default false).
  • includeClientLinkDetails — include client link details when includeLinks is true (default false).
  • runCmd — optional shell command after each write; ${fn} is replaced with the report path (~15s timeout). Runs as the Rallypoint user—treat as a privileged hook.

statusUpload

HTTP POST of status (and related) payloads to an ingest endpoint when baseUrl is non-empty.

  • baseUrl — destination URL (often includes ${id}). Empty disables upload.
  • timeoutSecs — request timeout (default 3, clamped roughly 1–30).

Useful for central dashboards; ensure the URL is trusted and reachable from the RP host.

linkGraph

Creates a Graphviz file representing links.

  • enabled — default false.
  • fileName — path of the Graphviz file. Blank means no file. ${id} is substituted with the Rallypoint ID.
  • minRefreshSecs — minimum seconds between file updates.
  • includeDigraphEnclosure — surround output in a strict digraph enclosure (default true).
  • includeClients — include client links (default false).
  • coreRpStyling / leafRpStyling / clientStyling — Graphviz styling for core Rallypoints, leaf Rallypoints, and clients.
  • runCmd — optional command after each write (${fn} = path).

routeMap

Periodic JSON dump of the RP’s routing table—handy for debugging mesh paths.

  • enabled — default false
  • fileName — output path (${id} supported)
  • minRefreshSecs — minimum refresh interval (default 5)
  • runCmd — optional post-write command (${fn})

streamStatsExport

Export per-stream counters on an interval.

  • enabled — default false
  • format — 0 = CSV, 1 = JSON
  • fileName — output path; may include ${id} and ${instance}
  • intervalSecs — default 60
  • resetCountersAfterExport — if true, zero counters after each export
  • runCmd — present in schema/samples; current rallypointd does not execute this hook—use an external watcher on the file if you need post-processing

externalHealthCheckResponder

For load balancers and network management systems that probe health by opening a TCP connection (often closing it immediately).

  • listenPort — TCP port for health-check connections.
  • immediateClose — close the connection immediately when true (some health checkers require this).

certificate

Security is fundamental to Rallypoints, largely via X.509 certificates. The certificate and key in this section are required for operation.

  • certificate — PEM content of the X.509 certificate, or a reference: @ followed by a file path, or @certstore:// followed by a certificate-store element name.
  • key — PEM private key associated with the certificate (same @ / @certstore:// rules).

additionalIdentities

Optional array of extra named TLS identities (certificate/key pairs, often for SNI or multi-tenant front ends) beyond the primary certificate. If any identity fails to load at startup, the Rallypoint aborts. Leave empty unless you need multiple server identities. Connecting clients (sni on the Rallypoint connection object) and Rallypoint peers (sni on the peering RallypointPeer object) send that name as TLS SNI; if it matches an identity name (case-insensitive), that certificate is presented. If SNI is omitted or unmatched, the primary certificate is used.

For a full walkthrough with multi-domain examples, Engage Engine group configuration, and peering scenarios, see Rallypoint Additional Identities and SNI.

tls

TLS settings for connections with clients and Rallypoint peers.

  • verifyPeers — ask for and verify the far-end X.509 certificate (mutual authentication and certificate-based message signing). Enable this.
  • allowSelfSignedCertificates — accept self-signed peer certificates. Generally leave this disabled.
  • caCertificates — array of CA certificates used to verify clients (PEM text or @ / @certstore:// references).
  • crlSerials — array of revoked certificate serials (a Rallypoint-local CRL). Strings are case-insensitive and must look like xx:xx:xx:xx:xx:xx:xx:xx. Examples in this article are for demonstration only.
  • subjectRestrictions / issuerRestrictions — serialized restriction lists on certificate Subject / Issuer. Present in schema but not enforced by the current Rallypoint TLS path. Use CA verification, crlSerials, and group / extended group restrictions for access control today.

fipsCrypto

FIPS 140-3-related settings. When enabled, the process loads the OpenSSL 3.1.2 FIPS Provider (rts-fips), which is NIST-validated under CMVP certificate #4985. See Engage Security.

  • enabled — activate FIPS mode when true.
  • path — absolute path to the directory containing the rts-fips FIPS module (directory, not the file name).
  • debug — extra FIPS diagnostics when supported.
  • curves — elliptic curves in FIPS mode. Default is NIST-approved secp521r1; secp384r1 and secp256r1 may also be used. Example for all three: secp521r1:secp384r1:secp256r1.
  • ciphers — TLS ciphers in FIPS mode. Default TLS_AES_256_GCM_SHA384; TLS_AES_128_GCM_SHA256 may also be used. Example: TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256.

licensing and featureset

Some capabilities (for example static reflectors in licensed deployments) are gated by licensing and an optional signed feature set. See also Engage Licensing.

  • licensing — entitlement, key, activationCode, deviceId, manufacturerId as provided by RTS / your OEM packaging.
  • featureset — signature, optional lockToDeviceId, and features array. Invalid or missing features can block licensed capabilities unless overridden via sysFlags (not recommended).

tcpTxOptions

How packets are sent on TCP/TLS (and related unicast) sockets. JSON key is tcpTxOptions (older docs sometimes said txOptions—that name is not accepted).

  • priority — QoS-related transmit priority (default often voice = 3). See Engage and Network Quality Of Service.
  • ttl — IP Time-To-Live. Sample configs often use 128; code may default differently if omitted.

multicastTxOptions

How packets are sent over multicast (for example when reflecting).

  • priority — QoS-related transmit priority. See Engage and Network Quality Of Service.
  • ttl — IP TTL for multicast (default 1). Understand what changing multicast TTL means in your environment before you change it.

udpStreaming

Optional UDP path for media while control stays on TLS/TCP. Full discussion, crypto types, NAT external address fields, and fallback behavior are in Rallypoint Link Options. Crypto details are in Engage Security.

  • enabled — turn UDP media streaming on or off (default off in many shipped configs).
  • cryptoType — shared-key mode 1–4 (AES or ChaCha20, full or indexed IV). Unsupported values disable UDP streaming.
  • listenPort — UDP listen port (default 7444). Open this on firewalls when enabled.
  • keepaliveIntervalSecs — UDP keepalive interval.
  • priority / ttl — transmit priority and TTL for UDP media packets.
  • ipv4 / ipv6 — per-family enablement and optional external address/port for NAT or load-balancer front ends.

When UDP streaming is off (or negotiation fails), media remains on the TLS/TCP link.

igmpSnooping

Optional IGMP querier/snooping so the RP can track local multicast interest.

  • enabled — default false
  • queryIntervalMs — default 125000
  • subscriptionTimeoutMs — 0 uses an RFC-derived timeout

Requires a build with IGMP support (RTS_RP_SUPPORT_IGMP); otherwise enabling this can prevent startup. Platform-dependent—validate on your OS before production use.

discovery

Optional discovery of radio/gateway/session assets on the local network. The RP starts discovery workers when ssdp, sap, cistech, and/or trellisware is enabled (interface typically falls back to multicastInterfaceName). Discovered groups are intended to feed reflection when forwardDiscoveredGroups is fully effective—today prefer explicit reflectors for production.

Sub-object Role
magellan Engage Magellan discovery (certs/TLS). Present in schema; not the primary gate in RP discovery startup.
ssdp SSDP discover/advertise (default broadcast 255.255.255.255:1900, age timeout often 30s).
sap Session Announcement Protocol (default 224.2.127.254:9875).
cistech Cistech gateway discovery.
trellisware Trellisware radio discovery.

Each protocol generally has enabled, interfaceName, addressing, timeouts, and (where relevant) advertising intervals or security blocks. Leave all disabled unless you are integrating those ecosystems.

advertising

mDNS / DNS-SD advertisement of this Rallypoint (default service _rallypoint._tcp.local.).

  • enabled — default false
  • hostName — empty ⇒ system hostname
  • serviceName — DNS-SD service type
  • interfaceName — NIC for mDNS
  • port — 0 ⇒ use listenPort
  • ttl — advertisement TTL (default 60)

When enabled, domain information from domainName / extraDomains can appear in TXT records so clients can find the right RP.

websocket

WebSockets are a standard way to run a persistent, bi-directional byte stream over HTTP-friendly infrastructure—load balancers, reverse proxies, CDNs, and other gear that already knows how to terminate TLS and steer or route streams. Rallypoints offer an optional WebSocket listener for exactly those environments: when you want clients (or intermediaries) to reach an RP using path-based or host-based stream steering, sticky sessions, or other WebSocket-aware front-end features that plain raw TCP is awkward to hang behind.

Under the covers, a WebSocket connection is still just a regular TCP (usually TLS) socket with an extra handshake up front (the HTTP Upgrade that turns the connection into a WebSocket). Once that handshake completes, Rallypoint does not keep speaking HTTP. It reverts to the native Rallypoint TCP protocol—the same control- and media-plane messaging you’d get on the main listenPort. The only ongoing difference is framing: a small WebSocket header (and the usual WebSocket data mangling required by the standard) wraps those native RP messages so middleboxes stay happy.

So: same RP protocol, same security model you configure below—delivered on a port and path shape that WebSocket-capable infrastructure understands.

  • enabled — default false
  • listenPort — default 8443
  • requireTls — default true
  • requireClientCertificate — default false (enable for mutual auth if you turn WebSockets on)
  • certificate / key — identity for the WebSocket listener (often a distinct certstore entry)

Open the WebSocket port on firewalls only if you use this path. Prefer client certificates in any untrusted network. Keep the primary TLS listenPort (default 7443) for direct native connections when you don’t need stream steering.

quic

QUIC is an optional UDP transport for the same Rallypoint control protocol used on TCP/TLS. Phase 1 uses one bidirectional reliable stream (ALPN rts-rallypoint-v1). There is no HTTP/3 and no QUIC DATAGRAM for media.

The Rallypoint reuses its existing TLS certificate. Engage clients select QUIC with "protocol": 2 or a quic://host:port URL. Peers set protocol to 2 on RallypointPeer.

  • enabled — default false
  • listenPort — UDP, default 7443 (same number as TCP listenPort; open UDP as well as TCP)

FIPS mode is allowed. Groups/ciphersuites stay on the approved P-256/P-384/P-521 and AES-GCM set already enforced by Crypto::createSslCtx.

openssl s_client without QUIC options is not a valid probe. On OpenSSL 3.5:

openssl s_client -quic -alpn rts-rallypoint-v1 -connect rp.example.com:7443

nsm

JSON key nsm embeds Network State Machine settings so an RP could participate in resource election with peer gateways/bridges.

  • Accepts a full NSM node object or a legacy flat NSM configuration shape.
  • Standalone nsmd / the Python tooling described in Using nsm runs a complete NSM process (scripts, status upload, and so on).
  • Important: current rallypointd parses nsm but does not start an embedded NSM election loop the way Engage Bridge Service does. Treat RP nsm as reserved/schema-ready until your release notes say otherwise. For HA with bridging, use EBS embedded NSM and/or the standalone tooling in Using nsm.

rtiCloud

Optional Rally Tactical (RTI) cloud enrollment for token/heartbeat/telemetry integration.

  • enabled — default false
  • enrollmentCode — from your RTI provisioning
  • serviceBaseUrlPrefix — default prod.com (resolved under the RTI cloud naming scheme)

Leave disabled unless you are onboarding this RP to RTI Cloud.

rxCapture and txCapture

PacketCapturer objects for dumping received or transmitted packets to files (enabled, maxMb, filePrefix).

These keys appear on RallypointServer and in sample conf. The Engage Engine uses this type for groups; current rallypointd does not apply RX/TX capture from these server keys. Enabling them on the RP today likely has no effect—use host-level tcpdump/Wireshark or Engine-side capture where supported.

tuning

Low-level memory and object-pool caps for RTP/blob/buffer processors (maxPooledRtpMb, maxPooledRtpObjects, maxActiveRtpObjects, and similar for blobs/buffers).

  • All-zero in config means “use RP defaults.”
  • At start the RP substitutes practical defaults (on the order of tens of MB pooled and thousands of active objects) when zeros are present.
  • Mis-tuning can OOM or thrash under load—leave at defaults unless you are memory-constrained and measuring.

multicastRestrictions

How multicast addresses are restricted—whitelist or blacklist. More in Multicast Reflection and Whitelisting And Blacklisting Using Restrictions.

  • type — 1 whitelist, 2 blacklist (0 often means unrestricted in samples).
  • elements — array of objects, each with multicast address/port pairing for RX and TX.

groupRestrictionAccessPolicyType

Values 0 or 1 control the default for group registration. 0 (default) is permissive: unless restricted by groupRestrictions / extendedGroupRestrictions, registration is allowed. 1 is strict: registration is denied unless the group is listed in groupRestrictions (and optionally further constrained in extendedGroupRestrictions).

groupRestrictions

Restrict which group identifiers are allowed or denied.

  • type — 1 whitelist, 2 blacklist.
  • elements — array of Engage group ID strings (exact match).
  • elementsType — may appear in shipped JSON; basic group restrictions ignore it and always treat elements as literal group IDs. Regex / cert matching belongs under extendedGroupRestrictions.

For extensive examples—including permissive vs strict policy, certificate access tags, mesh peering behavior, and troubleshooting—see Rallypoint Group Restrictions.

extendedGroupRestrictions

Fine-grained control within groups that groupRestrictions already addresses—typically based on the connecting client's X.509 certificate. See Extended Group Restrictions below, and the dedicated developer note Rallypoint Group Restrictions for full examples.

  • id — group ID this rule set applies to.
  • restrictions — array of restriction objects for that group.
  • type — 1 whitelist, 2 blacklist (within each restriction object).
  • elementsType — how to interpret elements (see the extended restrictions discussion).
  • elements — array of strings interpreted per elementsType.

staticReflectors

Array of multicast reflectors maintained for the lifetime of the process. More in Multicast Reflection. Static reflection may require a valid licensing / featureset in licensed deployments.

Meshing

OK great—you've set up a Rallypoint and lots of users are connecting. But you're running out of CPU because thousands of users are talking away. Or you have groups of people in different parts of the world who need their own Rallypoints but still want to talk to each other. Or you need extra Rallypoints for failover and redundancy. Or something else...

The answer is generally: add more Rallypoints.

And you want those Rallypoints to interconnect. That's meshing.

For this example, assume we want a bunch of Rallypoints in a cloud such as Amazon Web Services (AWS), available to users around the world. We don't want users locked to a particular Rallypoint—any user can connect to any available Rallypoint, and the Rallypoints forward traffic among themselves so it looks like one big cloud Rallypoint.

First we need something that front-ends the Rallypoints with a single DNS name (or IP) users connect to—say cloudrp.example.com. We'll use Amazon's Elastic Load Balancer for that discussion.

Ideally we'd use Rallypoints' ability to forward onto a multicast backbone:

                                                          |
                              (c1)      +------+          |
                        +-------------> |rp0001| <------> |
                        |               +------+          |
                        |                                 |
                        |                                 |
                 +-------------+                          | multicast
  c1 ----------> |             |  (c2)  +------+          | backbone
  c2 ----------> |Load Balancer| -----> |rp0002| <------> |
  c3 ----------> |             |        +------+          |
                 +-------------+                          |
                        |                                 |
                        |                                 |
                        |      (c3)     +------+          |
                        +-------------> |rp0003| <------> |
                                        +------+          |
                                                          |

Sadly, many cloud providers (including AWS) don't support IP multicast. Multicast can also be a pain to operate, so unicast is often simpler.

We still want that logical layout—so we build a Rallypoint mesh: Rallypoints connect directly to each other for traffic forwarding. Not unlike IP multicast, except the Rallypoints themselves do the "multicasting."

Each cloud Rallypoint connects to every other. When a client connects (via the load balancer) to one Rallypoint, its traffic is forwarded to peers that need it—similar to multicast.


                              (c1)      +------+         (pc)
                        +-------------> |rp0001| <---------------+
                        |               +------+                 |
                        |                   ^                    |
                        |                   | (pc)               |
                 +-------------+            |                    |
  c1 ----------> |             |  (c2)      +------->  +------+  |
  c2 ----------> |Load Balancer| ------------------->  |rp0002|  | (pc)
  c3 ----------> |             |            +------->  +------+  |
                 +-------------+            |                    |
                        |                   | (pc)               |
                        |                   v                    | 
                        |      (c3)     +------+                 |
                        +-------------> |rp0003| <---------------+
                                        +------+         (pc)

Common questions:

  • Does every Rallypoint forward all traffic to all peers? That would be wasteful. Rallypoints subscribe to each other per stream. If client 1 on RP1 and client 3 on RP3 share a stream, that traffic flows between RP1 and RP3—not via RP2.
  • What about security on peer links? They're TLS connections like client links, with the same mutual X.509 authentication and encryption.
  • Doesn't latency go up? A little—but Rallypoints are packet routers; they don't process payloads. Extra delay is typically on the order of microseconds.

Configuration

Meshing configuration is straightforward: certificate info plus peer details for the mesh.

Example:

{
   "peers":[
      {
         "id": "cloudrp0001",
         "enabled":true,
         "host": 
         {
            "address": "cloudrp0001.example.com",
            "port": 7443
         },

         "certificate": 
         {
            "certificate":"@certstore://someOtherCert",
            "key":"@certstore://someOtherCert"
         }
      },

      {
         "id": "cloudrp0002",
         "enabled":false,
         "host": 
         {
            "address": "cloudrp0002.example.com",
            "port": 7443
         }
      }
   ]
}

peers

Array of peer objects:

  • id — unique peer ID; should match that peer's id in its own configuration.
  • enabled — whether others should connect to this peer. Useful for disabling a peer without removing it from the file.
  • host/address — DNS name or IP of the peer.
  • host/port — TCP port to connect to; must match that peer's listenPort.
  • certificate — optional X.509 certificate and private key for this peer (instead of the default).
  • sni — optional TLS Server Name Indication sent when dialing this peer. Must match an additionalIdentities name on the far-end Rallypoint to select a non-default server certificate. Sent in the clear in the TLS ClientHello. Leave empty to use the far end's primary certificate.

Multicast Reflection

As in Meshing, a Rallypoint can join the local multicast network as a backbone between mesh nodes. It can also forward other multicast traffic—especially useful when bridging unicast endpoints (Engage Engines, Rallypoints) and multicast endpoints.

Engage entities can exchange multicast voice with non-Engage entities that speak standards such as RTP and codecs like G.711, AMR, and so on.

A classic use case: a two-way radio gateway on multicast, and Engage clients that need to talk to that system. Configure an Engage group for the gateway's codec and the same multicast address/port. If multicast flows cleanly between gateway and clients (often because they're on the same network), it just works.

Example: three Engage clients (c1, c2, c3) on the same multicast network as a gateway that bridges one radio talkgroup to bidirectional multicast 234.5.6.7:15000 over RTP with G.711 µ-law, without RTP encryption (common):

           multicast network
-------------------------------------
        ^              ^   ^    ^  
        |              |   |    |
        v              v   v    v
   +---------+        c1   c2   c3  
   | gateway |          
   +---------+          
        ^               
        |               
        v
 +--------------+
 | radio system |
 +--------------+

Matching codecs and multicast addressing with the gateway is the whole trick.

Under the covers Engage also uses a group ID for that group. Keep that in mind for later.

Simple, Local Reflection

Now suppose we don't want Engage users (c1, c2, c3) on multicast. We want them to connect over unicast to a Rallypoint that is on the multicast network with the gateway.

Not as contrived as it sounds—clients may be unable to join multicast because of OS limits, admin policy, and so on.

           multicast network
-------------------------------------
        ^                    ^
        |                    |
        v                    v
   +---------+          +---------+
   | gateway |          |         | <---------> c1
   +---------+          |   rp    | <---------> c2
        ^               |         | <---------> c3
        |               +---------+
        v
 +--------------+
 | radio system |
 +--------------+

How does the Rallypoint know which multicast/port to use, and which Engage group that maps to?

It can learn from clients. When a client connects and registers for a group, it passes multicast info with that registration. If multicast forwarding is allowed and the address/port is permitted (see restrictions below), the Rallypoint sets up a reflector: unicast traffic from clients (and peers) is reflected to multicast and vice versa.

When the first interested client connects, the Rallypoint creates the reflector and keeps it until nothing references it. Later registrations for the same group reuse it. When the last interested client disconnects, the reflector stops.

You mainly need to allow multicast reflection (off by default for security and bandwidth reasons).

Use the same group ID on all clients. Different groups that share the same multicast address/port may each talk to the radio system, but they won't talk to each other through Engage.

Getting A Little More Sophisticated

Engage clients on the Internet need to reach your radio system? Same idea: put the Rallypoint where it can reach multicast and where external clients can reach its TLS port (for example in a DMZ). Assign the public-facing NIC to interfaceName and the multicast NIC to multicastInterfaceName (same NIC is fine if your topology allows). Or proxy via another Rallypoint. Many layouts work; here's the DMZ-style picture:

                                     firewall
      internal network                  |
           multicast network            |  internet
-------------------------------------   |
        ^                    ^          |
        |                    |          |
        v                    v          |
   +---------+          +---------+     |
   | gateway |          |         | <--------> c1
   +---------+          |   rp    | <--------> c2
        ^               |         | <--------> c3
        |               +---------+     |
        v                               |
 +--------------+                       |
 | radio system |                       |
 +--------------+                       |
                                        |

Behavior matches the internal case—no client-side multicast required.

Static Reflection

Often you don't want clients to know anything about LMR multicast addressing—especially with many gateways worldwide. Teach the Rallypoint instead via staticReflectors: map a group ID to local multicast RX/TX. Clients only need the group ID.

You need the multicast address/port and the group ID from whoever builds the group configs for clients. That varies by vendor/app; for this example assume {1e351ac4-7915-4144-9545-82d60c9cfe4e}.

   .
   .
   "staticReflectors": 
   [
      {
         "id":"{1e351ac4-7915-4144-9545-82d60c9cfe4e}",
         "rx":
         {
            "address":"234.5.6.7",
            "port":15000
         },
         "tx":
         {
            "address":"234.5.6.7",
            "port":15000
         }
      }
   ]
   .
   .

Restart the Rallypoint. The reflector for that group stays up for the process lifetime. Clients registering for that group ID get traffic from other clients and from multicast; their TX goes to multicast as well.

Architecturally this matches the previous diagram—the difference is the Rallypoint knows the multicast mapping at startup instead of learning it from the first client.

Connecting Into A Mesh

To expose statically reflected multicast into a core Rallypoint mesh, peer the local Rallypoint into the mesh and have clients connect to the mesh. Engines and Rallypoints deliver traffic where it's needed; only the local Rallypoint needs to know the gateway's multicast setup.

You can hang multiple radio sites off the mesh: each site has its own gateway, network, and multicast addressing; each is a distinct group ID for clients. On each site Rallypoint (rp01, rp02), configure a static reflector for the local radio system, set isMeshLeaf to true, and peer into the cloud mesh—typically to the load balancer in front of the core, not to every core node individually.

---------------------------------
        ^               ^
        |               |
        v               v
   +---------+     +---------+
   | gateway |     |   leaf  |
   +---------+     |   rp01  |<--------------+
        ^          |         |               |
        |          +---------+               |
        v                                    |
 +--------------+                            |
 | radio 01     |                            v
 +--------------+                       +---------+ 
                                        |         | <----> c1
                                        | cloud   | <----> c2
                                        | mesh    | <----> c3
                                        |         |
                                        +---------+
                                             ^
---------------------------------            |
        ^               ^                    |
        |               |                    |
        v               v                    |
   +---------+     +---------+               |
   | gateway |     |   leaf  |               |
   +---------+     |   rp02  | <-------------+
        ^          |         |
        |          +---------+
        v                       
 +--------------+
 | radio 02     |
 +--------------+

Set the local Rallypoint as a mesh leaf with isMeshLeaf. If you don't, the core mesh may not forward as expected and you can see one-way audio.

Whitelisting And Blacklisting Using Restrictions

To cut bandwidth, tighten security, and keep accidental (or hostile) registrations out, Rallypoints support restrictions—rules that allow or deny access to multicasts and groups.

For multicast, type 1 is a whitelist (only listed address/port pairs work). type 2 is a blacklist (everything except the listed pairs). The same idea applies to group IDs on a Rallypoint.

Remember groupRestrictionAccessPolicyType: 0 is permissive by default; 1 is strict (deny unless listed). Extended restrictions only refine access after a group is otherwise in play.

Multicast Restrictions

From the example configuration earlier:

   .
   .
   "multicastRestrictions": 
   {
      "type":1,
      "elements":
      [
         {
            "rx": 
            {
               "address":"234.1.2.3",
               "port":25000
            },
            "tx": 
            {
               "address":"234.1.2.3",
               "port":25000
            }
         },

         {
            "rx": 
            {
               "address":"234.5.6.7",
               "port":17222
            },
            "tx": 
            {
               "address":"234.5.6.7",
               "port":17222
            }
         }
      ]
   },
   .
   .

With type 1, only 234.1.2.3:25000 and 234.5.6.7:17222 are allowed; everything else fails. Set type to 2 to allow all multicasts except those.

NOTE: Whitelist (type 1) with an empty elements list effectively disables multicasting. The Rallypoint logs a warning at startup and continues.

Group Restrictions

Same pattern for group IDs:

   .
   .
    "groupRestrictions":
   {
      "type":1,
      "elements":
      [
         "{58e3e468-c0e1-4ad8-86d2-9931251e6ea0}",
         "{b7694a4f-9724-44a6-ae57-c63232ad1f57}"
      ]
   },
   .
   .

Only those two group IDs may register—clients and peer Rallypoints. Bonus: when Rallypoints peer, they exchange group restrictions in the handshake so they can filter what they advertise across the mesh, which saves bandwidth and overhead.

type 2 blacklists specific group IDs and allows the rest.

NOTE: Whitelist (type 1) with no group IDs turns off essentially all group routing. The Rallypoint logs a fatal error and aborts—because without groups it has nothing useful to do.

If you use staticReflectors together with restrictions, include those reflector multicast address/port pairs in (or at least don't blacklist them from) multicastRestrictions, and allow the corresponding group IDs in groupRestrictions.

Extended Group Restrictions

Basic group restrictions are a blunt instrument: whitelist a group and anyone with a valid TLS certificate can register. Extended restrictions add who may use that group, based on the client's X.509 certificate—group-level firewalling without passwords or user databases.

Example: group {58e3e468-c0e1-4ad8-86d2-9931251e6ea0} is for schools. You only want educators on it. Restrict by certificate tags:

"extendedGroupRestrictions": [
{
   "id": "{58e3e468-c0e1-4ad8-86d2-9931251e6ea0}",
   "restrictions": [
      {
         "type": 1,
         "elementsType": 2,
         "elements": [
            "-educators\\b",
            "-teachers\\b",
            "-instructors\\b"
         ]
      }
   ]
}
]

Those tags are embedded in the client certificate presented at connect time (via certificate extensions / access tags—your PKI process defines how they get there). If none of the tags match, registration for that group is denied; other groups may still be allowed.

To also exclude certificates issued for Washington or Idaho (ST in the Subject), add a deny rule:

"extendedGroupRestrictions": [
{
   "id": "{58e3e468-c0e1-4ad8-86d2-9931251e6ea0}",
   "restrictions": [
      {
         "type": 1,
         "elementsType": 2,
         "elements": [
            "-educators\\b",
            "-teachers\\b",
            "-instructors\\b"
         ]
      },
      {
         "type": 2,
         "elementsType": 5,
         "elements": [
               "(ST=WA)|(ST=ID)"
         ]
      }
   ]
}
]

How evaluation works

  1. Group must be allowed first — via permissive policy, or presence on a group whitelist / absence from a blacklist, per groupRestrictionAccessPolicyType and groupRestrictions.
  2. If there is no extendedGroupRestrictions entry for that group ID, anyone who passed step 1 may register.
  3. If there is an entry, each object in restrictions is applied. Think of them as layered gates:
    • Within one restriction object, elements are alternatives (OR): matching any element satisfies that object for a whitelist, or triggers denial for a blacklist.
    • Across multiple restriction objects, the client must satisfy the combination: whitelist objects grant a candidate pass; blacklist objects can still veto. In the school example, you need an educator/teacher/instructor tag and you must not match the WA/ID subject rule.
  4. Peers — mesh peers also present certificates. Extended rules apply to peer-driven registrations the same way when those registrations are subject to the local Rallypoint's policy. Combined with handshake exchange of basic groupRestrictions, this keeps unwanted group traffic off the mesh early.

elementsType values

Value Meaning
0 Literal group IDs (not regex)
1 Group ID regex patterns
2 Access-tag regex patterns in the client certificate
3 Client certificate serial-number regex
4 Client certificate fingerprint regex
5 Regex against the client certificate Subject
6 Regex against the issuing CA certificate Subject

Except for elementsType 0, matching uses PCRE2 regular expressions (Perl Compatible Regular Expressions). Regex is powerful and easy to get subtly wrong—test before you rely on it in production. General background: Regular expression.

In JSON strings, backslashes must be escaped: write \\b in the file so the Rallypoint sees \b.

Regex Gotchas

A tag pattern like -educators also matches -educatorsInPhysics and -educatorsWhoDriveCars. For an exact tag, use a word boundary: -educators\\b in JSON (that is the pattern -educators\b).

Other practical tips:

  • Prefer anchored or bounded patterns when matching Subject DN fragments (ST=WA can appear in unexpected places if you're careless).
  • Fingerprint and serial patterns (types 3 / 4) are usually for break-glass allow/deny of specific devices—not day-to-day role control.
  • Type 6 (issuer Subject) is useful when multiple CAs issue client certs and you only trust one organization's CA for a sensitive group.
  • After changing restrictions, restart or reload per your ops practice and confirm with a known-good and known-bad certificate. Check Rallypoint logs for denied registrations.
  • Extended rules do not replace TLS mutual auth—they refine group access after the link is already authenticated.

Monitoring Examples

Every organization monitors services differently. The status file and logs described under Operation are the building blocks; here is a concrete example you can adapt.

Bash script for periodic status display

Here's a script developed for a Rallypoint mesh in Amazon Web Services. Use or modify it for your environment.

#!/bin/bash

#-------------------------------------------------------------------------
# Rallypoint Status Display
# Copyright (c) 2020 Rally Tactical Systems, Inc.
#
# This script periodically reads a status file produced by a Rallypoint
# and displays useful statistics.  We're making use of JQ in this
# script so if you don't already have JQ, install it.
#
# This script has been tested on RedHat-style distros and works quite
# well, your mileage may vary (slight) on other distros.  Feel free
# to modify accordingly.
#
# In fact, this script was developed for RTS' internal use for a Rallypoint
# mesh hosted in Amazon Web Services.  So there's a little bias toward AWS
# here.  Mostly, though, this script should work on most Linux distros.
#
# Here's an example of a JSON status file.  We will be using some of the
# elements from it in our script.
#
#{
#  "connections": {
#    "active": 49,                              <-- Number of active network connections
#    "total": 574                               <-- Total network connections for the lifetime of the process
#  },
#  "healthChecks": {
#    "count": 10823,                            <--- Number of healthcheck connections we've had from the load balancer
#    "rate": 0.834,                             <--- Instantaneous rate of health check connections per second
#    "rateEma": 1.353                           <--- Exponential moving average of the rate of health check connections
#  },
#  "id": "i-07ff7082e6969e259",                 <--- The ID of the Rallypoint
#  "links": {
#    "clients": {
#      "count": 48                              <--- Number of active client connections
#    },
#    "peers": {
#      "configuredConnectedCount": 1,           <--- Number of peer (mesh) connections the RP has connected that were statically configured
#      "configuredCount": 1,                    <--- Number of peer (mesh) connections the RP is statically configured for
#      "count": 1,                              <--- Number of active peer (mesh) connections
#      "leafConnectedCount": 0,                 <--- Number of connected leaf RP nodes
#      "list": [                                <--
#        {
#          "address": "172.31.11.22:7443",      <--- Address of a peer
#          "id": "i-0aaa5cefaa5cbd870",         <--- ID of a peer
#          "state": 2,                          <--- Connection state of the peer (0=NotLinked, 1=InProgress, 2=LinkedOutbound, 3=LinkedInbound)
#          "type": 1                            <--- Connection type of the peer (1=Core, 2=Leaf)
#        }
#      ]
#    }
#  },
#  "queue": {
#    "avgExecNanos": 0,                         <--- Average operation execution time in nanoseconds
#    "depth": 0,                                <--- Number of operations pending in the queue (should be 0 or close to it)
#    "maxDepth": 172,                           <--- Maximum number of operations that backed up in the queue during the process' lifetime
#    "maxExecNanos": 0,                         <--- Maximum operation execution time in nanoseconds
#    "minExecNanos": 0,                         <--- Minimum operation execution time in nanoseconds (should always be 0)
#    "ops": {
#      "count": 168145,                         <--- Number of operations processed during the process' lifetime
#      "rate": 38.867,                          <--- Instantaneous rate of operations per second
#      "rateEma": 5.035                         <--- Exponential moving average of rate of operations per second
#    },
#    "spuriousWakeups": 16243,                  <--- Number of times the queue woke up with no work to do
#    "wakeUps": 155164                          <--- Total number of times the queue woke up
#  },
#  "routing": {
#    "blobs": {
#      "rx": {
#        "bytes": 41455816,                     <--- Incoming bytes of routed streamed data
#        "packets": 123354                      <--- Incoming packets of routed streamed data
#      },
#      "tx": {
#        "bytes": 1252696896,                   <--- Outgoing bytes of routed streamed data
#        "packets": 3815596                     <--- Outgoing packets of routed streamed data
#      }
#    },
#    "streams": 7                               <--- Number of streams/groups registered for routing
#  },
#  "rx": {
#    "bytes": 43615050,                         <--- Overall incoming bytes of data (routed and otherwise)
#    "packets": 139700,                         <--- Overall incoming packets of data (routed and otherwise)
#    "rate": 59306.667,                         <--- Overall RX byte rate/second
#    "rateEma": 3011.565                        <--- Exponential moving average of overall RX byte rate/second
#  },
#  "ts": 1584653616,
#  "tx": {
#    "bytes": 1256490445,                       <--- Overall outgoing bytes of data (routed and otherwise)
#    "packets": 3831874,                        <--- Overall outgoing packets of data (routed and otherwise)
#    "rate": 2679872,                           <--- Overall TX byte rate/second
#    "rateEma": 65829.295                       <--- Exponential moving average of overall TX byte rate/second
#  }
#}   
#-------------------------------------------------------------------------   


# This script is running on Amazon Web Services, for we use this magic little curl
# call to retrieve this instance's ID.  This same instance ID is used by the Rallypoint
# to identify itself in our mesh.  For your environment, your Rallypoint ID may be something
# else - like a Kubernetes cluster instance ID, a host name, an IP address, or
# some other unique identifier
RP_ID=`curl -s http://169.254.169.254/latest/meta-data/instance-id`
if [ "${RP_ID}" == "" ]; then
        echo "ERROR: Cannot determine Rallypoint instance ID"
        exit 1
fi

# Our Rallypoint periodically writes its status to the /tmp directory into a JSON
# file named with the instance ID followed by "_status.json".  The default is
# 30 seconds but your configuration may be different.
FN="/tmp/${RP_ID}_status.json"

# We will check the JSON file every so often for changes
UPDATE_CHECK_SECS=10

# Just some internal variables
SHOW_TABLE_HEADER=1
LAST_TS=""

# Load our values
function loadVals()
{
        # Determine how long the process has been running
        UPTIME=`ps axo etime,%cpu,%mem,cmd | grep 'rallypointd -id' | grep -v grep | awk '{split($0,a); print a[1];}'`

        # Parse our JSON into an array of strings
        VALUE_ARRAY=(`jq '.ts,.connections.active,.connections.total,.healthChecks.rate,.links.clients.count,.links.peers.count,.routing.streams,.routing.blobs.rx.packets,.routing.blobs.tx.packets,.queue.depth,.queue.maxDepth,.queue.ops.count,.queue.ops.rate,.queue.ops.rateEma' "${FN}"`)

        # Grab the elements from the array
        TS=${VALUE_ARRAY[0]}
        CONN_ACTIVE=${VALUE_ARRAY[1]}
        CONN_TOTAL=${VALUE_ARRAY[2]}
        CONN_HC_RATE=${VALUE_ARRAY[3]}
        CONN_CLIENTS=${VALUE_ARRAY[4]}
        CONN_PEERS=${VALUE_ARRAY[5]}

        RT_STREAMS=${VALUE_ARRAY[6]}
        RT_RX_BLOBS=${VALUE_ARRAY[7]}
        RT_TX_BLOBS=${VALUE_ARRAY[8]}

        Q_DEPTH=${VALUE_ARRAY[9]}
        Q_MAX_DEPTH=${VALUE_ARRAY[10]}
        Q_OP_COUNT=${VALUE_ARRAY[11]}
        Q_OP_RATE=${VALUE_ARRAY[12]}
        Q_OP_RATE_EMA=${VALUE_ARRAY[13]}
}

# Display our pretty table
function showTable()
{
        # Only show the table header the first time this function is called
        if [ "${SHOW_TABLE_HEADER}" == "1" ]; then
                SHOW_TABLE_HEADER=0
                echo "Rallypoint Status Display for node ${RP_ID}"
                echo "Copyright (c) 2020 Rally Tactical Systems, Inc."

                echo "------------------------------ ------------ ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------"
                echo "                     Timestamp       Uptime Actv Conns  Tot Conns    HC Rate    Clients      Peers    Streams   RX Blobs   TX Blobs    Q Depth      Q Max      Q Ops     Q Rate Q Rate EMA"
                echo "------------------------------ ------------ ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------"
        fi

        # Make a human readable as-of timestamp
        ASOF=`date -d@${TS}`

        printf "%30s %12s %10d %10d %10.2f %10d %10d %10d %10d %10d %10d %10d %10d %10.2f %10.2f\n" \
                "${ASOF}" \
                "${UPTIME}" \
                "${CONN_ACTIVE}" \
                "${CONN_TOTAL}" \
                "${CONN_HC_RATE}" \
                "${CONN_CLIENTS}" \
                "${CONN_PEERS}" \
                "${RT_STREAMS}" \
                "${RT_RX_BLOBS}" \
                "${RT_TX_BLOBS}" \
                "${Q_DEPTH}" \
                "${Q_MAX_DEPTH}" \
                "${Q_OP_COUNT}" \
                "${Q_OP_RATE}" \
                "${Q_OP_RATE_EMA}"
}

# We'll go round and round here
while [ true ]; do
        # Load values from the JSON file
        loadVals

        # Is the timestamp different from the last time we read it?  If not,
        # there's no need to print a new line in the table
        if [ "${TS}" != "${LAST_TS}" ]; then
                LAST_TS="${TS}"

                # Show the table (actually just one line in the table)
                showTable
        fi

        # Go to sleep for a little while
        sleep ${UPDATE_CHECK_SECS}
done

Example output from this script looks as follows (in this case Engage clients were connecting and disconnecting frequently and only two peers were configured in the mesh):

$ /mnt/efs/shared/rallypointd/rpstatus.sh

Rallypoint Status Display for node i-07ff7082e6969e259
Copyright (c) 2020 Rally Tactical Systems, Inc.
------------------------------ ------------ ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
                     Timestamp       Uptime Actv Conns  Tot Conns    HC Rate    Clients      Peers    Streams   RX Blobs   TX Blobs    Q Depth      Q Max      Q Ops     Q Rate Q Rate EMA
------------------------------ ------------ ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
  Fri Mar 20 02:15:13 UTC 2020     08:08:58         40       1921       0.93         39          1          5     691490   26112909          0        172     847366      73.10       6.91
  Fri Mar 20 02:15:43 UTC 2020     08:09:18         46       1927       0.80         45          1          5     693859   26214356          0        172     849964      86.60       6.91
  Fri Mar 20 02:16:13 UTC 2020     08:09:49          2       1934       0.90          1          1          5     695355   26285940          0        172     851725      58.70       6.91
  Fri Mar 20 02:16:43 UTC 2020     08:10:19         11       1943       0.97         10          1          5     695834   26288993          0        172     852348      20.77       6.92
  Fri Mar 20 02:17:13 UTC 2020     08:10:49         16       1948       0.60         15          1          5     696703   26299841          0        172     853350      33.40       6.92
  Fri Mar 20 02:17:43 UTC 2020     08:11:19         21       1953       0.97         20          1          5     697811   26318936          0        172     854607      41.90       6.92
  Fri Mar 20 02:18:13 UTC 2020     08:11:49         28       1960       0.97         27          1          5     699118   26350193          0        172     856093      49.53       6.92
  Fri Mar 20 02:18:43 UTC 2020     08:12:19         38       1970       0.67         37          1          5     700926   26409198          0        172     858123      67.67       6.93
  Fri Mar 20 02:19:13 UTC 2020     08:12:49         46       1978       1.03         45          1          5     703316   26507774          0        172     860747      87.47       6.93
  Fri Mar 20 02:19:43 UTC 2020     08:13:19          3       1985       0.83          2          1          5     704682   26573009          0        172     862369      54.07       6.94
  Fri Mar 20 02:20:13 UTC 2020     08:13:49         12       1994       0.93         11          1          5     705205   26576656          0        172     863034      22.17       6.94
  Fri Mar 20 02:20:43 UTC 2020     08:14:19         21       2003       0.77         20          1          5     706156   26591777          0        172     864154      37.33       6.94
  Fri Mar 20 02:21:14 UTC 2020     08:14:49         28       2010       0.80         27          1          5     707295   26618490          1        172     865472      43.93       6.94
  Fri Mar 20 02:21:45 UTC 2020     08:15:19         36       2018       1.03         35          1          5     708779   26663941          0        172     867168      56.53       6.95
  Fri Mar 20 02:22:15 UTC 2020     08:15:49         40       2022       0.73         39          1          5     710849   26739301          0        172     869438      75.67       6.95
  Fri Mar 20 02:22:45 UTC 2020     08:16:19         45       2027       0.87         44          1          5     713398   26844522          0        172     872205      92.23       6.95
  Fri Mar 20 02:23:15 UTC 2020     08:16:49          3       2036       0.83          2          1          5     714856   26912651          0        172     873919      57.13       6.96
  Fri Mar 20 02:23:45 UTC 2020     08:17:19          8       2041       0.93          7          1          5     715374   26915291          0        172     874548      20.97       6.96
  Fri Mar 20 02:24:15 UTC 2020     08:17:49         15       2048       0.87         14          1          5     716251   26925560          0        172     875570      34.07       6.96
  Fri Mar 20 02:24:45 UTC 2020     08:18:20         23       2056       0.83         22          1          5     717385   26946167          0        172     876875      43.50       6.96
  Fri Mar 20 02:25:15 UTC 2020     08:18:50         33       2066       1.00         32          1          5     718847   26984451          0        172     878544      55.63       6.97
  Fri Mar 20 02:25:45 UTC 2020     08:19:20         39       2072       0.87         38          1          5     720672   27048113          0        172     880577      67.77       6.97
  Fri Mar 20 02:26:15 UTC 2020     08:19:50         46       2079       0.77         45          1          5     723055   27146906          0        172     883192      87.17       6.98
  Fri Mar 20 02:26:45 UTC 2020     08:20:20          2       2084       1.00          1          1          5     724372   27208731          0        172     884746      51.80       6.98
  Fri Mar 20 02:27:15 UTC 2020     08:20:50         10       2092       0.93          9          1          5     724956   27212535          0        172     885465      23.97       6.98
  Fri Mar 20 02:27:45 UTC 2020     08:21:20         18       2100       0.90         17          1          5     725827   27224674          0        172     886493      34.27       6.99
  Fri Mar 20 02:28:16 UTC 2020     08:21:50         25       2107       0.87         24          1          5     727009   27249273          0        172     887845      45.07       6.99
  Fri Mar 20 02:28:46 UTC 2020     08:22:20         32       2114       0.97         31          1          5     728467   27291089          0        172     889499      55.13       7.00
  Fri Mar 20 02:29:16 UTC 2020     08:22:50         38       2120       0.93         37          1          5     730261   27353807          0        172     891498      66.63       7.01

Troubleshooting

Connections & Registrations

If a client or peering Rallypoint can't reach a Rallypoint, start by proving basic reachability before diving into application logs. The simplest check is opening a TLS connection to the Rallypoint with a browser or openssl.

Example: reach rp.example.com on the default listen port 7443 (or whatever you set for listenPort).

Use a web browser

Use https so the browser speaks TLS:

https://rp.example.com:7443

A typical Chrome result looks like:

This site can’t provide a secure connection

rp.example.com uses an unsupported protocol.

ERR_SSL_VERSION_OR_CIPHER_MISMATCH

Unsupported protocol

The client and server don't support a common SSL protocol version or cipher suite.

That usually means DNS resolved, TCP to 7443 worked, and TLS negotiation started. The Rallypoint is not a web server, so the handshake won't finish cleanly—and that's fine. You've proven the RP is reachable.

Use the OpenSSL command-line

openssl s_client -host rp.example.com -port 7443

You want something like:

CONNECTED(00000006)
depth=0 C = US, ST = Washington, L = Seattle, O = "Rally Tactical Systems, Inc.", OU = "(c) 2019 Rally Tactical Systems, Inc. - For authorized use only", CN = Rallypoint Factory Default Certificate, emailAddress = [email protected]
verify error:num=20:unable to get local issuer certificate
.
.
.

openssl will complain about certificate verification. That's OK for this test—you're confirming reachability, not completing a mutual-auth Engage session.

If it didn't work...

Use the browser or openssl error to narrow it down.

Process-related

  • rallypointd may not be running—confirm the process is up.
  • The process may be hung—restart it and check logs / status file.

Network-related

  • DNS may not resolve—try the host's IP address.
  • The RP host may block inbound TCP to 7443 (or your listenPort)—open it on the host firewall.
  • Something on the path (routers, switches, VLANs, proxies, cloud security groups) may block TCP to that port.

If UDP media streaming is enabled and voice is one-way or stuck on TCP, also verify UDP reachability to udpStreaming.listenPort (default 7444) and any external NAT mappings. See Rallypoint Link Options.

That worked but there's still no connection

Here connect means a TCP link comes up and then drops almost immediately.

  • Certificate mismatch: the RP rejects the client's (or peer's) X.509 cert, or the client rejects the RP's. Check logs on both sides. Use the openssl output above to inspect what the RP presents.
  • Load limits: the RP may refuse new connections when configured limits or CPU thresholds are hit. Check limits in rallypointd_conf.json, plus logs and the status file.
  • Capture with Wireshark or tcpdump to see where the link dies.

The RP link does come up but some (or all) groups don't connect

Networking and the TLS link are good, but group registration fails. Common causes:

  • Registration exceeds configured limits—check config, logs, and status.
  • groupRestrictions / extendedGroupRestrictions / groupRestrictionAccessPolicyType deny the group or the caller's certificate—check those sections and the logs for denials.
⚠️ **GitHub.com Fallback** ⚠️