Run your own WebX11 (fork / mirror guide)

This is the supported way to stand up an independent WebX11 instance on your own host. It is not the chroot demo in container.md — that one is deprecated.


1. What you get

Layer Components
Web nginx vhost (~95 locations), tenant spaces under /spaces/*, WebDAV with auth_request, TLS
APIs 16 systemd units: marketplace, skills hub, feedback, payment, AI gateway, seo forge, module API, code viewer, monitor, federation, auth, dav-auth, hermes sessions, UBAN dashboard + watcher
Security failure2ban jails, UFW, dotfile blocking, UBAN threat detection, rate limits
Data SQLite users DB, marketplace + skills file roots under /var/lib/webx11
Ops /etc/cron.d/webx11 minimal maintenance set, log rotation, verify.sh

Everything runs as one unprivileged user (WX11_USER, default darkbot) with group www-data. No root daemons beyond systemd units.

2. Requirements

3. Quickstart

# 1. get the code (shallow clone; history is not needed to run an instance)
git clone --depth 1 https://git.webx11.duckdns.org/webx11.git /opt/webx11-src
cd /opt/webx11-src

# 2. describe your instance
cp deploy/wx11.conf deploy/wx11.conf.local
$EDITOR deploy/wx11.conf.local      # WX11_DOMAIN, WX11_USER, WX11_TLS_EMAIL, WX11_NODE_NAME

# 3. install (DNS must already resolve)
printf '%s\n' 'a-long-admin-password' > /root/wx11-admin.pass && chmod 600 /root/wx11-admin.pass
sudo deploy/install.sh --conf deploy/wx11.conf.local --source /opt/webx11-src \
     --tls --admin you@example.com --admin-password-file /root/wx11-admin.pass
sudo rm -f /root/wx11-admin.pass

# 4. prove it
sudo deploy/verify.sh --conf deploy/wx11.conf.local

verify.sh must end with ALL CHECKS PASSED. If it does not, the failing line names the component; do not open the site to users before it passes.

Install order (why it looks like that)

  1. packages → 2. user + dirs → 3. vendor UBAN/monitor → 4. web tree → 5. render templates →
  2. users DB → 7. systemd units → 8. HTTP bootstrap vhost → certbot → real vhost →
  3. firewall → 10. node identity + admin user → verify.

Vendor comes before units because units are skipped when their ExecStart path is missing. The HTTP bootstrap vhost exists because a vhost with ssl_certificate fails nginx -t before certbot has issued anything — that would leave nginx down mid-install.

Every step is idempotent; re-running install.sh is safe. --dry-run prints every action and writes nothing.

4. What the installer changes on your box

Path What
/etc/apt installs nginx, python3 (+bcrypt), sqlite3, certbot, fail2ban, ufw, rsync, jq
$WX11_ROOT your web tree (rsync, no --delete — uploads and users.db survive)
/var/lib/webx11/{skills,marketplace}/files user file roots (outside the web root)
/var/lib/webx11/federation node identity + peer registry, mode 700
/etc/systemd/system/*.service 16 units, mode 640 root:www-data
/etc/nginx/sites-available/<domain> vhost (symlinked into sites-enabled)
/etc/nginx/conf.d/wx11-zones.conf rate-limit zones (http context)
/etc/cron.d/webx11 minimal maintenance set (no Hermes jobs)
/etc/webx11/ per-service env files (secrets), mode 750 root:www-data
/opt/uban, /usr/local/bin/webx11-monitor vendored security + monitor components
UFW allow SSH port (detected), 80, 443; enable
fail2ban installed and enabled (jail.local from the distro defaults)

5. Ports

Loopback only; nginx proxies. The map is fixed — one instance per host.

8889 proxy-api 8903 seoforge
8890 monitor status 8904 dav-auth
8891 module-api 8905 code viewer
8893 hermes sessions 8906 marketplace
8896 auth 8907 skills
8899 payment 8908 feedback
8902 ai gateway 8909 federation

Each unit receives Environment=WX11_PORT=<its port>, and the Python services read it via api/wx11_config.service_port(), so a single service can be moved by editing the unit — but nginx expects the table above.

6. DNS and TLS

  1. Create the A record before running the installer.
  2. install.sh --tls runs certbot with --nginx after the bootstrap vhost is live.
  3. Renewal is handled by the distro timer: systemctl list-timers | grep certbot.
  4. Certificate expiry is also checked by deploy/verify.sh (fails under 14 days).

Without --tls the instance serves plain HTTP; verify.sh will report the certificate check as failed, which is expected until you add TLS.

7. Secrets

Nothing secret belongs in the repo or in a unit file. Each unit includes:

EnvironmentFile=-/etc/webx11/<service>.env

So on a fork you create, for example:

sudo install -d -m 750 -o root -g www-data /etc/webx11
sudo install -m 640 -o root -g www-data /dev/null /etc/webx11/auth-api.env
sudo -e /etc/webx11/auth-api.env     # add the payment-provider key from YOUR dashboard
sudo chmod 640 /etc/webx11/auth-api.env
sudo systemctl restart auth-api

Optional components (payment, Telegram alerting) can stay unconfigured: their routes return errors, the rest of the site works.

For alerting, WX11_ALERT_CHAT_ID must be set in /etc/webx11/telegram.env; without it notify.py prints one line and sends nothing (so a fork never messages the maintainer).

8. Becoming a mirror / peer

# announce yourself to another instance (their operator must approve)
sudo -u darkbot python3 api/federation-cli.py add-peer https://main.example.org

# see what is waiting on you
sudo -u darkbot python3 api/federation-cli.py pending
sudo -u darkbot python3 api/federation-cli.py approve <node_id>

# who am I peered with, and are they alive?
sudo -u darkbot python3 api/federation-cli.py list
sudo -u darkbot python3 api/federation-cli.py health

Approval is manual on both sides by design — there is no auto-accept. Your node’s public identity is https://<your-domain>/federation/node.json; the peer list is /federation/peers.json. deploy/federation-health.sh runs from /etc/cron.d/webx11 every 15 minutes and prints only on a status transition.

9. Upgrading

sudo deploy/update.sh --conf deploy/wx11.conf.local

It refuses to run on a dirty worktree unless you pass --force-dirty, backs up the users DB with sqlite3 .backup, re-renders units and the rate-limit snippet, restarts services and runs verify.sh. See UPGRADING.md.

10. Uninstall

sudo systemctl disable --now webx11-marketplace webx11-skills webx11-feedback webx11-payment \
  webx11-aigateway webx11-seoforge webx11-module-api webx11-codeviewer webx11-monitor \
  webx11-federation auth-api dav-auth uban-dashboard uban-watcher
sudo rm -f /etc/systemd/system/{webx11-*,auth-api,dav-auth,uban-*}.service
sudo rm -f /etc/nginx/sites-available/<domain> /etc/nginx/sites-enabled/<domain> \
           /etc/nginx/conf.d/wx11-zones.conf /etc/cron.d/webx11
sudo rm -rf /etc/webx11 /opt/uban /usr/local/bin/webx11-monitor /var/lib/webx11
sudo rm -rf $WX11_ROOT              # your data — back up /var/lib/webx11 first
sudo ufw delete allow 80/tcp; sudo ufw delete allow 443/tcp
sudo systemctl daemon-reload && sudo systemctl reload nginx

Back up first: sqlite3 $WX11_ROOT/admin/security-test/users.db ".backup /root/users.db".

11. Known limits