                       DOOR HUB 0.1.0 - RUNNING A HUB
                For sysops who want to host games for other BBSes

CONTENTS
  1. How a hub works
  2. What you need
  3. Preparing the door BBS (Synchronet)
  4. Installing the hub on Linux
  5. Installing the hub on Windows
  6. Settings
  7. Games
  8. Member BBSes
  9. Players
 10. Opening the hub to the internet
 11. Logs and statistics
 12. Security
 13. Backups and moving the hub
 14. Troubleshooting
 15. Command reference


1. HOW A HUB WORKS
------------------
  member BBS --RLogin--> connector --SSH tunnel--> hub --RLogin--> door BBS
  (caller)               (on the member's machine)   (doorhub)   (your Synchronet)

  - Each member BBS has a hub account: an SSH user name and password and
    a system tag. The hub accepts only RLogin tunnels on these accounts;
    there is no shell, no file access and no other forwarding.
  - The member's BBS sends: the caller's password, [TAG]Name, and a game
    code in the RLogin terminal type. The hub checks that the tag belongs
    to the account that logged in.
  - The hub keeps its own list of players. The first time it sees
    [TAG]Name it makes a user on your Synchronet BBS (the "door BBS")
    called "Name (TAG)", with a random password only the hub knows.
  - It then logs that user in to your Synchronet by RLogin with the
    terminal type xtrn=CODE. Synchronet's logon.js runs that door straight
    away and hangs up when it ends, so the player never sees your BBS's
    menus.
  - If the member BBS asked for a game, the player goes back to their BBS
    when it ends. If they came through the hub menu, they return to the
    menu.

All the hub's data is in one folder: hub.json (settings, member BBSes,
games), players.json, the SSH host key host_ed25519, mkuser.js (written
by the hub each time it starts) and, for a service, doorhub.log.


2. WHAT YOU NEED
----------------
  - Linux (x64 or arm64) or Windows (64-bit) with a Synchronet BBS on the
    same machine. Door Hub was built and tested with Synchronet 3.22;
    any recent version with rlogin_auto_xtrn in logon.js works. This BBS is the "door BBS": the games run on it.
    It can be your main BBS, or a separate one just for the hub.
  - The doors you want to share, installed and working on that BBS.
  - For members outside your network: a fixed internet address or a DNS
    name, and one TCP port forwarded to the hub (2022 by default).
  - Permission to share each door with other BBSes. Your own doors and
    free software are fine. Check the licence of commercial doors: some
    registrations cover one BBS only.


3. PREPARING THE DOOR BBS (SYNCHRONET)
--------------------------------------
3.1 RLogin must be on. In ctrl/sbbs.ini, section [BBS]:

      Options = ... | ALLOW_RLOGIN
      RLoginPort = 513

    The hub connects from the same machine (127.0.0.1). You don't need to
    open port 513 to the internet for the hub.

3.2 Direct-to-door logins must be on. In ctrl/modopts.ini, section
    [logon]:

      rlogin_auto_xtrn = true

3.3 Check the doors. Each door you share needs an Internal Code (shown in
    SCFG > External Programs > Online Programs) and must be usable by a
    new user:
      - The section's and the program's Access Requirements must allow a
        new user. Blank, ANSI or NOT GUEST are fine; SYSOP is not.
      - Hub players are made with your BBS's normal new-user settings
        (security level, flags, time per day). If you limit new users,
        hub players are limited the same way.
    It doesn't matter which section a door is in: the hub starts it by its
    Internal Code. You can put the shared doors in their own section.

3.4 Doors that run multi-node (LORD, Ember Reach and so on) need
    Multiple Concurrent Users set, like for any busy BBS.

3.5 If your BBS asks new users questions at logon, that doesn't affect
    hub players: the hub makes them with jsexec, not through the new-user
    form.

3.6 Restart Synchronet (or let it recycle) after changing sbbs.ini or
    modopts.ini.


4. INSTALLING THE HUB ON LINUX
------------------------------
The hub must run as the same user as Synchronet, because it creates
users with Synchronet's jsexec. These steps assume that user is "sbbs".

  sudo mkdir /opt/doorhub
  sudo cp doorhub /opt/doorhub/
  sudo chown -R sbbs: /opt/doorhub
  sudo chmod 700 /opt/doorhub
  sudo ln -s /opt/doorhub/doorhub /usr/local/bin/doorhub
  sudo -u sbbs doorhub init

init makes /opt/doorhub/hub.json and the host key and prints the key's
fingerprint. Give that fingerprint to member sysops so they can check it.

If Synchronet is not in /sbbs:

  sudo -u sbbs doorhub set jsexec /path/to/sbbs/exec/jsexec
  sudo -u sbbs doorhub set sbbs_ctrl /path/to/sbbs/ctrl

Set your public name (what members put in ssh_host), add games and
members (sections 7 and 8), then try it in the foreground:

  sudo -u sbbs doorhub serve

Run it as a service. Edit doorhub.service (from the package) so User,
Group and paths match, then:

  sudo cp doorhub.service /etc/systemd/system/
  sudo systemctl daemon-reload
  sudo systemctl enable --now doorhub
  systemctl status doorhub

The admin commands (doorhub bbs ..., doorhub door ...) work while the
hub is running, and changes apply at once. Run them as the hub's user
(sudo -u sbbs doorhub ...) so the files keep the right owner. The folder
is /opt/doorhub unless you use -d folder or the DOORHUB_DIR environment
variable.


5. INSTALLING THE HUB ON WINDOWS
--------------------------------
  1. Make a folder C:\doorhub and copy doorhub.exe into it. (Another
     folder works; then add  -d C:\your\folder  to every command, or set
     the environment variable DOORHUB_DIR.)
  2. Open a Command Prompt as Administrator and run:
         cd C:\doorhub
         doorhub init
  3. If Synchronet is not in C:\sbbs:
         doorhub set jsexec D:\sbbs\exec\jsexec.exe
         doorhub set sbbs_ctrl D:\sbbs\ctrl
  4. Set the public name, add games and members (sections 7 and 8), then
     try it:
         doorhub serve
     Windows Firewall asks whether doorhub.exe may accept connections.
     Allow it on the networks your members connect from. Ctrl+C stops it.
  5. Install it as a Windows service so it starts with Windows:
         doorhub service install
         doorhub service start
     The service is "Door Hub" (DoorHub). It runs as Local System and
     restarts itself if it stops. Its log is C:\doorhub\doorhub.log.
     To stop or remove it:
         doorhub service stop
         doorhub service remove

Windows notes:
  - The hub creates Synchronet users with jsexec.exe. Local System can
    write to C:\sbbs by default. If Synchronet runs as another account and
    you have tightened permissions on its folders, set the DoorHub
    service to log on as that account (services.msc > Door Hub >
    Log On).
  - Keep doorhub.exe, jsexec.exe and the Synchronet folders on a local
    drive.


6. SETTINGS
-----------
  doorhub settings              show them
  doorhub set <name> <value>    change one

  hub_name              Name shown at the top of the hub menu.
  listen                Address and port the hub listens on. ":2022" means
                        every address, port 2022. Needs a hub restart.
  public_host           Your hub's internet name, put into the connector
                        ini that "bbs add" prints.
  public_port           The port members connect to (may differ from
                        listen if your router forwards another port).
  backend_rlogin        Your Synchronet RLogin server, normally
                        127.0.0.1:513.
  jsexec                Path to Synchronet's jsexec program.
  sbbs_ctrl             Path to Synchronet's ctrl folder.
  max_sessions_per_bbs  How many players one member BBS may have on the
                        hub at once. 0 means no limit. Default 10.
  new_user_comment      The comment set on users the hub creates, so you
                        can find them in your user editor.

Everything except listen applies straight away.


7. GAMES
--------
  doorhub door add <code> <INTERNAL CODE> "<name>"

  <code>        what member BBSes send to start this game, for example
                lord. Lower case letters, digits, - and _, up to 20.
  INTERNAL CODE the door's Internal Code on your Synchronet (SCFG).
  name          shown in the hub menu.

Examples (the Kalgoorlie Door Hub):

  doorhub door add lord  LORD     "Legend of the Red Dragon"
  doorhub door add reach REACH    "Ember Reach"
  doorhub door add seal  SEAL     "The Broken Seal"
  doorhub door add wake  LONGWAKE "The Long Wake"

Other commands:

  doorhub door list                 games, open or closed, play counts
  doorhub door disable <code>       close a game (it leaves the menu;
                                    direct requests get the menu instead)
  doorhub door enable <code>
  doorhub door rename <code> "<name>"
  doorhub door remove <code>

The menu lists open games by name, keys 1-9 then A-Z (no Q, which
returns the player to their BBS). Send your members the list of codes so
they can make direct menu entries (SYSOP.TXT section 6).

Test a new game yourself first: log on to your Synchronet as a new user
and run it, so you know the door works before members try it.


8. MEMBER BBSES
---------------
Adding a BBS:

  doorhub bbs add <TAG> "<BBS name>" [--user <ssh user>] [--contact "<text>"]

  TAG       2 to 8 letters or digits, shown in front of the BBS's
            players' names. Must be unique.
  --user    the SSH user name for the BBS (default: tag in lower case
            followed by _bbs). Lower case letters, digits, _ . and -.
  --contact anything that helps you reach the sysop (email, netmail).

The command prints a complete doorhub-connector.ini, with a new random
32-character password. The password is shown only this once: the hub
keeps only a bcrypt hash of it. Send the ini to the member sysop over a
private channel (email to a known address, netmail, encrypted file), not
in a public message base.

Example:

  $ doorhub bbs add EXB "The Example BBS" --contact "sysop@example.com"
  Added EXB (The Example BBS). The password is shown only now.

  ; doorhub-connector.ini for The Example BBS
  system_tag      = EXB
  ssh_username    = exb_bbs
  ssh_password    = 8kQm...
  local_interface = 127.0.0.1
  local_port      = 9998
  ssh_host        = hub.example.com
  ssh_port        = 2022
  rlogin_host     = 127.0.0.1
  rlogin_port     = 513

Looking after members:

  doorhub bbs list                 all members, enabled or not, last seen
  doorhub bbs show <TAG>           details and number of players
  doorhub bbs passwd <TAG>         new password (prints a new ini); the old
                                   one stops working for new logins
  doorhub bbs disable <TAG>        block the BBS. New sessions are refused
                                   at once, even through an open tunnel.
                                   Games already running carry on.
  doorhub bbs enable <TAG>
  doorhub bbs note <TAG> "<text>"  keep a note (shown by bbs show)
  doorhub bbs remove <TAG>         delete the account. Its players stay in
                                   players.json and on your Synchronet,
                                   so game progress isn't lost if the BBS
                                   comes back with the same tag.

"Last seen" is the last time the BBS's connector logged in. A connector
keeps one SSH connection open and reuses it for every caller, so this
changes when the connector starts or reconnects, not for every game.


9. PLAYERS
----------
  doorhub player list [TAG]         players, newest first: hub name, door
                                    BBS user, first and last seen, plays
                                    per game
  doorhub player reset "[TAG]Name"  forget the player's remote password.
                                    The next password that arrives for
                                    that name is kept.

When the hub first sees [TAG]Name it makes the Synchronet user
"Name (TAG)" (shortened to fit Synchronet's 25 characters, with a number
added if two players would get the same name). The user's password is
random and known only to the hub. Each session the hub runs mkuser.js
with jsexec to make sure the user exists and the password is right, so
if you delete a hub user in Synchronet it's simply made again (with a
fresh start in every game).

Each player's first password from their BBS is stored as a bcrypt hash.
A different password for the same name is refused with "Your Door Hub
password doesn't match". This stops a new caller who takes an old name on
a member BBS from playing as the old caller. When a member rebuilds their
user base, reset their players.

Player names may be up to 30 characters and can't contain [ or ].


10. OPENING THE HUB TO THE INTERNET
-----------------------------------
  1. Give the hub machine a fixed LAN address.
  2. On your router, forward one TCP port (2022, or another) to the hub
     machine's port 2022 (the listen setting).
  3. Give it a name. A DNS name (hub.example.com) is better than an IP
     address, because members don't need to change anything if your
     address changes.
  4. doorhub set public_host hub.example.com
     doorhub set public_port 2022   (the outside port)
  5. From outside your network, check the port answers:
       ssh -p 2022 test@hub.example.com
     It should ask for a password and refuse it. (That's the hub:
     it doesn't allow shells.)

Only port 2022 needs to be open. The Synchronet RLogin port does not.

The hub will see password guesses from the internet like any SSH
server. Every failed login is logged and delayed by two seconds, each
connection gets three tries, and member passwords are long and random,
so guessing isn't practical. A tool like fail2ban can also watch the log
lines "Login refused for ...".


11. LOGS AND STATISTICS
-----------------------
The hub logs to standard output (journalctl -u doorhub with the
provided unit, or the file it appends to), or to doorhub.log in the hub
folder when it runs as a Windows service. Lines look like:

  BBS KAL connected from 203.0.113.5:51800
  New player [KAL]Heisenberg -> "Heisenberg (KAL)"
  [KAL]Heisenberg started lord
  [KAL]Heisenberg left lord after 12m40s
  Login refused for "kal_bbs" from 198.51.100.7:40122
  [KAL] refused [KAL]Bob: BBS disabled or removed

doorhub door list shows total plays per game; doorhub player list shows
plays per player. Play counts are in players.json if you want to make
your own score lists.


12. SECURITY
------------
  - Members can only open RLogin tunnels. Shell, exec, subsystem (SFTP),
    port forwarding to other places and every other SSH request are
    refused. Whatever address a connector asks to reach, the hub only
    ever talks RLogin to its own door BBS.
  - A member can only send players with its own tag. A connection
    claiming another tag is refused and logged.
  - Member passwords and player passwords are kept only as bcrypt hashes.
  - Door BBS passwords for hub users are random and stored in
    players.json, which (like the whole folder) should be readable only
    by the hub's account.
  - The host key host_ed25519 identifies your hub. Keep it secret and back
    it up: if you lose it, every member's connector refuses the new key
    until they delete their doorhub_known_hosts.
  - Doors run on your Synchronet as ordinary users, with the same limits
    as any caller. Treat member players like callers: a door that lets a
    caller reach a shell or files is just as risky through the hub.


13. BACKUPS AND MOVING THE HUB
------------------------------
Back up the hub folder (hub.json, players.json, host_ed25519) and your
Synchronet user base and game folders. To move the hub to another
machine, stop it, copy the folder and the games, start it there, and
point your DNS name at the new machine. Because the host key comes along,
members don't need to change anything. A Linux hub folder works on
Windows and the other way round, apart from the jsexec and sbbs_ctrl
settings.


14. TROUBLESHOOTING
-------------------
"Sorry, your Door Hub account could not be set up"
    mkuser.js failed. The hub log shows jsexec's last lines. Check the
    jsexec and sbbs_ctrl settings, that the hub runs as the BBS's user,
    and that jsexec runs by hand:
      jsexec -c /sbbs/ctrl -n /opt/doorhub/mkuser.js "Test (TST)" pass123
    (Delete that test user afterwards.)

"Sorry, the games server is down"
    The hub couldn't connect to backend_rlogin. Is Synchronet running
    with RLogin on? (Section 3.1.)

"Sorry, the games server refused the login"
    Synchronet refused the RLogin login. Check Synchronet's log for the
    user "Name (TAG)". A user marked deleted or inactive in Synchronet,
    or an RLogin restriction, can cause this.

The player sees the Synchronet logon and then is disconnected, or sees
your main menu
    rlogin_auto_xtrn isn't on (section 3.2), or the INTERNAL CODE in
    "door add" doesn't match SCFG, or the door's access requirements
    stop new users (section 3.3). Synchronet's log shows
    "Unable to launch external" for a wrong code.

Members can't connect at all
    Check the port forward (section 10), the firewall on the hub machine,
    and that the hub is running (doorhub.log / journalctl). Their
    connector log usually says exactly what failed.

"listen tcp :2022: address already in use"
    Something else (perhaps another hub) uses the port. Change listen.

Changes to hub.json by hand
    You can edit hub.json with a text editor, but use the commands if you
    can: they lock the file so the running hub never reads half an edit.
    Don't edit password hashes by hand; use bbs passwd.


15. COMMAND REFERENCE
---------------------
  doorhub [-d folder] <command>

  init                          create hub.json and the host key
  serve                         run the hub
  service install|remove|start|stop
                                Windows service "DoorHub" (as Administrator)
  settings                      show settings
  set <name> <value>            change a setting (section 6)

  bbs add <TAG> "<name>" [--user u] [--contact c]
  bbs list | show <TAG> | passwd <TAG> | enable <TAG> | disable <TAG>
  bbs note <TAG> "<text>" | remove <TAG>

  door add <code> <INTERNAL> "<name>"
  door list | enable <code> | disable <code> | rename <code> "<name>"
  door remove <code>

  player list [TAG] | reset "[TAG]Name"

  help | version
