# HDStack multi-process deployment HDStack raises the aggregate hotspot capacity of one FreeDMR container by running two to five independent FreeDMR HBP workers. It does not raise the connection limit of an individual worker. Omitting `HDSTACK`, or setting `HDSTACK=1`, preserves the existing standard single-FreeDMR process layout. Setting `HDSTACK` to a value from 2 through 5 selects the generated multi-process layout: ```text hotspots -> proxy -> HBP workers -> no-HBP aggregator -> external FBP/OBP | +-> optional separate bridge.py instance worker reports -> reporting MUX -> existing dashboard/reporting consumer ``` The aggregator owns all OpenBridge/FBP links from the main `freedmr.cfg` and has no HBP `MASTER`, `PEER` or `XLXPEER` system. The optional `bridge.py` process retains its own configuration and rules. The reporting MUX reads only worker reports; aggregator and bridge reporting are not included. ## Container configuration Add these environment values to the standard `freedmr` service: ```yaml environment: - HDSTACK=2 - HDSTACK_BASEID=23400 ``` `HDSTACK_BASEID` is the aggregator server ID. Worker IDs are allocated by adding their one-based worker number. For the example above, the aggregator is `23400` and the two workers are `23401` and `23402`. With `HDSTACK=5`, worker IDs continue through `23405`. The separate `bridge.py` process is enabled by default. Disable it when the deployment does not require bridge rules or HBP peer/XLX connections: ```yaml environment: - HDSTACK_BRIDGE=0 ``` `HDSTACK_BRIDGE` accepts only `0` or `1`. It affects only multi-process mode. When disabled, no bridge configuration or rules file is required and no `bridge.py` process is started. Remove or disable any aggregator OpenBridge section intended only to connect to that bridge instance. When the bridge is enabled, multi-process mode requires its existing inputs: ```yaml volumes: - '/etc/freedmr/freedmr.cfg:/opt/freedmr/freedmr.cfg' - '/etc/freedmr/freedmr-bridge.cfg:/opt/freedmr/freedmr-bridge.cfg:ro' - '/etc/freedmr/rules-bridge.py:/opt/freedmr/rules.py:ro' ``` The paths can be changed with: ```yaml environment: - HDSTACK_BRIDGE_CONFIG=/opt/freedmr/freedmr-bridge.cfg - HDSTACK_BRIDGE_RULES=/opt/freedmr/rules.py ``` The main `freedmr.cfg` remains the sysop-facing aggregator and worker-template configuration. It must contain one enabled `[SYSTEM]` stanza in `MASTER` mode. All enabled and disabled `OPENBRIDGE` sections are copied to the aggregator; this includes external FBP/OBP links and the configured link to `bridge.py`. Other protocol systems, including `[SYSTEM]` and `[ECHO]`, are not copied to the aggregator. Each worker receives the same `[SYSTEM]` policy. Only its base HBP port is derived. Worker and aggregator configuration files are generated privately in `/dev/shm/freedmr-hdstack` when the container starts. The mounted source configuration and bridge files are not changed. When enabled, the bridge configuration must contain the reciprocal link to the aggregator and any `PEER` or `XLXPEER` systems used by `bridge.py`. Its `rules.py` remains the authority for bridge routing. Bridge reporting must either be disabled or use a port which does not collide with the worker reports or MUX. ## Worker ports The `[SYSTEM]` `PORT` and `GENERATOR` values define each worker range. With the standard values `PORT=54000` and `GENERATOR=100`: ```text worker 1 -> 54000..54099 worker 2 -> 54100..54199 worker 3 -> 54200..54299 worker 4 -> 54300..54399 worker 5 -> 54400..54499 ``` The generated ranges must fit within the UDP port range and must not collide with configured bridge, external OpenBridge or reporting ports. Workers connect to the aggregator over generated loopback OBP v1 links. Worker ports are `7001` through `7005`; reciprocal aggregator ports are `7101` through `7105`. These links are private to the container. ## Session assignment The proxy rotates new DMR IDs through the configured worker ranges. With two workers the assignments are: ```text new session 1 -> worker 1 new session 2 -> worker 2 new session 3 -> worker 1 new session 4 -> worker 2 ``` Within the selected worker range, the proxy retains its existing random choice of an available generated system port. A DMR ID remains pinned to that exact generated port for the lifetime of its active proxy session. Moving it would start a new HBP login and reset its BRIDGES and OPTIONS state. The proxy does not rebalance or fail over an active session. Once the session expires, a later login by the same DMR ID is a new assignment. Affinity is not persisted across proxy restarts. Existing explicit proxy configurations using `DestportStart` and `DestportEnd` continue to describe one range. The equivalent direct multi-range setting is: ```ini [PROXY] BackendPortRanges: [[54000, 54099], [54100, 54199]] ``` or: ```sh FDPROXY_BACKEND_PORT_RANGES='[[54000,54099],[54100,54199]]' ``` The standard HDStack entrypoint supplies this setting automatically. ## Proxy diagnostics In multi-worker mode the proxy logs each backend number and port range during startup. With the standard two-worker layout this includes: ```text (PROXY)(HDSTACK) Backend:1 ports:54000-54099. (PROXY)(HDSTACK) Backend:2 ports:54100-54199. ``` When `FDPROXY_CLIENTINFO=1`, as in the shipped Compose configuration, client assignment and removal messages include both the backend number and exact generated port. This makes round-robin assignment and retained session affinity visible without enabling packet-level debug logging. Single-backend startup output remains unchanged. ## Reporting MUX When reporting is enabled in the main configuration, worker report ports are derived from its `REPORT_PORT`. With the default output port `4321`: ```text MUX output -> 4321 worker 1 -> 4322 worker 2 -> 4323 worker 3 -> 4324 worker 4 -> 4325 worker 5 -> 4326 ``` The MUX preserves the existing netstring reporting protocol and the main configuration's `REPORT_CLIENTS` allow-list. It merges worker configuration and bridge snapshots and relays worker bridge events. System names are prefixed with the stable worker number to prevent collisions: ```text 1:SYSTEM-001 2:SYSTEM-001 ``` Only the system-name field is changed. The reporting protocol contains trusted Python pickle data and must remain on a trusted network. A failed or malformed worker reporting source is isolated from healthy sources. Reporting failure does not affect the proxy or DMR traffic. If main reporting is disabled, worker reporting and the MUX are both disabled. ## Limitations HDStack deliberately provides no least-connections balancing, active backend failover, live migration, persistent affinity or automatic reconfiguration. A failed worker does not cause its active sessions to move to another worker. The separate bridge config and rules remain sysop-managed; the runtime generator does not create routing policy.