====== Run a Multi-Host BBS ======

Synchronet lets a **single logical BBS** — one system, one user base, one
message base, one file base — be served by **several physical hosts at once**,
even hosts running **different operating systems**. Vertrauen runs this way: a
Linux machine and a Windows machine both run Synchronet against the //same//
install directory, sharing all of the BBS's data in real time. To a caller
they are one BBS; behind the scenes the work is spread across the machines.

The whole trick is:

  * Put the **entire Synchronet install directory on a single network share** (Vertrauen uses [[https://www.samba.org/|Samba]]/%%SMB%%) and mount it on every participating host.
  * Give each host its **own start-up configuration file** so it knows //which// servers and //which// nodes it is responsible for — this is the **one** file that differs per host.
  * Use [[howto:mqtt|MQTT]] to **monitor and control every host from one client**.

This page describes how those pieces fit together.

===== One install directory, many hosts =====

Everything Synchronet reads and writes at run time lives under the install
directory — ''ctrl'' (configuration), ''data'' (message bases, user base,
run-time state), ''text'', ''exec'', ''mods'', the per-node directories, and so
on. Normally that directory sits on the machine's local disk. In a multi-host
setup it instead lives on **one file server**, and **every host mounts that same
directory over the network**.

Because all hosts open the same message base, user base and node directories at
the same time, the file server must mediate **file locking** between them.
Synchronet relies on that locking to keep concurrently-accessed BBS data
consistent, so the share has to be configured for **strict, un-cached locking**.
On Samba, the share Vertrauen exports uses these settings:

<code ini>
[sbbs]
   path = /path/to/sbbs
   read only = no
   # --- required for safe concurrent multi-host access ---
   oplocks = no
   level2 oplocks = no
   kernel oplocks = no
   smb2 leases = no
   smb3 directory leases = no
   strict locking = yes
   sync always = yes
</code>

The essential ones are **disabling every kind of oplock and lease** — so no
client is ever allowed to cache locks or file data that another host has since
changed — and **''strict locking = yes''**, so byte-range locks are honoured on
every read and write. **''sync always = yes''** additionally forces each write
to disk before it is acknowledged. These can live in ''[global]'' or on the
share itself. They are deliberately conservative — **do not** relax them to
chase performance; they are what keep two hosts from corrupting each other's
writes.

==== Even the file server itself mounts the share ====

This is the part that trips people up: the machine that **is** the %%SMB%%/Samba
server must **also reach the install directory through the network mount**, not
through the local filesystem path.

If the server host used the files directly on local disk while the other hosts
went through %%SMB%%, the local accesses would bypass the very locking the %%SMB%%
server is coordinating — and sooner or later two hosts step on each other and
corrupt data. So on Vertrauen even the Samba server loopback-mounts its own
share (a CIFS mount of ''%%//127.0.0.1/<share>%%'') and runs Synchronet out of that
mount point. **Every** host, without exception, goes through the one %%SMB%% server.
That single server is the sole arbiter of every lock.

===== The one per-host file: the start-up ini =====

Almost every file under the install directory is shared **identically** by all
hosts. There is exactly **one** configuration file that must differ per host:
the Terminal Server start-up file, **''ctrl/sbbs.ini''**.

This is the file that says //what this host does// — which servers it runs
(Terminal, Mail, FTP, Web, Services…), what ports/interfaces they bind, its log
level, and which **node numbers** it serves. Naturally those answers are
different on each machine: on Vertrauen the Windows host runs the public Web
server while the Linux host runs the Terminal and mail servers, and each host
owns a **distinct range of node numbers** so they never collide.

You do **not** keep a separate off-share copy of this file on each machine.
Instead Synchronet resolves it **by hostname**, automatically. When it starts,
it looks in ''ctrl'' for the first of these that exists:

  ctrl/sbbs.<full.host.domain>.ini      (e.g. sbbs.vert.synchro.net.ini)
  ctrl/sbbs.<hostname>.ini              (e.g. sbbs.vert.ini)
  ctrl/sbbs.<platform>.ini              (e.g. sbbs.Linux.ini, sbbs.Win32.ini)
  ctrl/sbbs.ini                         (the fallback, used if none of the above)

So all of the per-host start-up files live **on the shared volume too**, side by
side, and each host simply picks up its own by name. On Vertrauen the ''ctrl''
directory contains ''sbbs.vert.ini'' (the Windows host), ''sbbs.git.ini'' (a
Linux host), and so on; ''sbbs.ini'' itself is only the generic fallback.

**Configuring each host's start-up file from one place.** You don't have to log
into every machine to set it up. [[util:scfg|SCFG]]'s //Servers// and //Nodes//
menus edit the start-up ini, and by default they operate on the running host's
own variant (resolved by hostname, exactly as above). To edit a //different//
host's file from one common machine, start SCFG with the ''-host=<name>''
option: ''scfg -host=vert'' edits ''ctrl/sbbs.vert.ini'', ''scfg -host=git''
edits ''ctrl/sbbs.git.ini'', and plain ''scfg'' edits the local host's. So a
single machine — with the shared ''ctrl'' directory mounted — can configure
every host's servers and nodes.

**Note:** this hostname/platform resolution is specific to the **start-up file
''sbbs.ini''** — the file that says what each host runs. The SCFG-managed
configuration files (''main.ini'', ''xtrn.ini'', ''msgs.ini'', and the rest)
are loaded by fixed name and **cannot** have a per-hostname variant; they are
always shared by every host. That is by design: those files describe the BBS
itself — identical on every host — while ''sbbs.ini'' describes each host's
individual role. One consequence worth noting: the [[howto:mqtt|MQTT]] broker
settings live in the shared ''main.ini'', so every host necessarily talks to the
**same** broker (see below).

Everything else — ''text.dat'' and the rest of ''ctrl'', all of ''data'',
''text'', ''exec'', ''mods'', the message/file bases, the user base — is one
shared copy that every host reads and writes together. Add a message area, edit
a menu, install a door: it is instantly live on every host.

===== Dividing the work across hosts =====

Because the hosts share one ''data'' directory, they also share the per-node
directories (''node1'', ''node2'', …). Assign each host a **non-overlapping**
range of node numbers in its own ''sbbs.<hostname>.ini'' so two hosts never try
to run the same node. Every host can still **see** every node's status (the node
directories are shared), which is what lets a user on the Linux host page or
chat with a user connected to the Windows host, list who's online across the
whole system, and so on — it behaves as one BBS because it //is// one BBS.

===== Cross-platform hosts: keep the shared config portable =====

When the hosts run **different operating systems** — a Windows host and a Linux
host sharing one ''ctrl'' directory, as Vertrauen does — every value in the
shared configuration has to make sense on **all** of them. A few rules keep the
one shared config portable:

  * **Use forward slashes in every configured path — never backslashes.** Forward slashes are valid on every platform: the Windows and DOS file APIs accept them natively and *nix uses them, so a ''/''-delimited path works everywhere with no translation needed. A backslash is //not// portable — on *nix it's an ordinary filename character, not a separator, so a backslash path simply breaks there. (Synchronet substitutes backslashes only where it must — when it builds a path for an externally-executed DOS program that expects them — so you never need to write them yourself.) Write ''xtrn/lord/start.js'', not ''xtrn\lord\start.js''.
  * **Never put a drive letter in a shared path.** ''C:/sbbs/...'' resolves on Windows but not on Linux or BSD, which have no drive letters. Use paths **relative** to the install directory, or Synchronet's built-in command-line specifiers (e.g. ''%!'' for the exec directory in an external-program command line), so the same value resolves on every host no matter its OS or where the shared volume is mounted. A companion specifier, ''%.'', expands to ''.exe'' on Windows/OS2/DOS and to **nothing** on *nix — so a single shared command line like ''%!mygame%.'' launches ''mygame.exe'' on a Windows host and ''mygame'' on a *nix host.
  * **Gate platform-specific features with ARS OS keywords.** Because the external-program (door) list is shared, add an Access Requirement String OS keyword so each item is only offered on hosts that can actually run it. The keywords are ''WIN32'', ''UNIX'', ''LINUX'', ''OS2'', and ''DOS'' (true only where 16-bit DOS programs can run). Synchronet evaluates them per connection against the host the caller landed on, so one shared door list automatically shows the right subset on each host. Vertrauen's ''ctrl/xtrn.ini'' uses exactly this — e.g. ''ars=WIN32 AND DOS'' for an old DOS door, ''ars=UNIX AND SYSOP'' for a native *nix tool.
  * **Turn off DOS support per host with ''NO_DOS''.** The ''DOS'' ARS keyword above is also gated by the Terminal Server's ''NO_DOS'' option, set in the ''[BBS] Options'' line of that host's ''sbbs.<hostname>.ini''. Set it on hosts that can't (or shouldn't) run 16-bit DOS doors and every ''DOS''-gated item disappears there automatically. On Linux, running DOS doors additionally requires DOSEMU, so a Linux host without it should keep ''NO_DOS'' set.

The pattern is the same throughout: one shared config, written portably, with
per-host differences expressed through the **hostname-resolved ''sbbs.ini''** and
**ARS keywords** rather than by maintaining separate configurations.

===== Monitor and control every host from one client (MQTT) =====

With the work spread across machines, you don't want to log into each one to see
what's happening. Synchronet's [[howto:mqtt|MQTT]] integration solves this
neatly for a multi-host system, because the topic tree is **namespaced by
host**:

  sbbs/<sysid>/host/<hostname>/server/<server>/...
  sbbs/<sysid>/host/<hostname>/event/...

All of Vertrauen's hosts share the same system ID (''VERT'') and the same broker
(configured once in the shared ''main.ini''), and each host publishes under its
own ''host/<hostname>'' branch. That means a **single** MQTT client, subscribed
at the system level, sees **every** host at once:

  # everything, every host, every server
  mosquitto_sub -h <broker> -t 'sbbs/VERT/#' -v
  # just one host
  mosquitto_sub -h <broker> -t 'sbbs/VERT/host/vert/#' -v
  # e.g. every host's Web server activity
  mosquitto_sub -h <broker> -t 'sbbs/VERT/host/+/server/web/#' -v

The same topic tree carries **control** topics, again scoped per host, so from
that one client you can recycle or pause a server **on a specific host**, clear
the failed-login list system-wide, inject a message into a node, and so on —
without touching the machines individually. See [[howto:mqtt|the MQTT how-to]]
for the full topic tree, the monitoring vs. controlling topics, and broker
authentication/TLS options.

This is what makes a multi-host Synchronet manageable: one shared install to
configure, and one MQTT client to watch and drive all of it.

===== Caveats =====

==== macOS SMB client ====

As of this writing, the **macOS %%SMB%% client does not participate reliably** in
this scheme. Its handling of the strict, un-cached file locking that Synchronet
requires across hosts differs enough from the Linux and Windows clients that a
macOS host mounting the shared volume is **not currently recommended** as a
participating BBS host — you risk the very lock-coordination problems the shared
setup is built to avoid. Stick to Linux and/or Windows hosts for now. (A macOS
machine can of course still connect to the BBS as an ordinary **client**; the
limitation is only about //hosting// off the shared volume.)

==== Keep the share's locking strict ====

It bears repeating: the conservative locking settings on the share are
load-bearing. They may look like a performance handicap, but relaxing oplocks,
leases, or strict locking to speed up file access will eventually cost you a
corrupted message base or user record when two hosts collide. Leave them strict.

===== See Also =====

  * [[howto:mqtt|Monitoring and controlling Synchronet with MQTT]]
  * [[howto:systemd|Running Synchronet as a systemd service]] (Linux hosts)
  * [[dir:index|The Synchronet directory hierarchy]]
  * [[:howto:|How-To index]]

{{tag>multihost samba smb cifs mqtt cluster}}
