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.
deploy/install.shdeploy/verify.shdeploy/update.shFEDERATION.mdPLATFORMS.md — proot Debian is
supported, native Termux is experimental until the on-device acceptance
script passes.| 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.
# 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.
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.
| 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) |
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.
install.sh --tls runs certbot with --nginx
after the bootstrap vhost is live.systemctl list-timers | grep certbot.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.
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).
# 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.
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.
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".
videos/*.wav|mp4)
committed on purpose. Do not promise a small clone.gunesuygarligi/data/subscribers.json is tracked and
contains client data. Empty it on a fork unless you are that
client.webx11.duckdns.org.
deploy/rebrand.py --from … --to … rewrites live pages
(dry-run by default; skips archive/,
_data/memory/, .git).FEDERATION.md.install.sh deliberately does not overwrite an existing
vhost: pass --force-nginx when you really mean to replace
it (it backs up the old file first).deploy/lib/supervisor.py instead of units. Same
services.tsv, same templates — only the process parent
changes.