# 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.

- Installer: `deploy/install.sh`
- Health gate: `deploy/verify.sh`
- Upgrade path: `deploy/update.sh`
- Peer your instance to others: see [`FEDERATION.md`](FEDERATION.md)
- Run it on a phone (Android/Termux): see [`PLATFORMS.md`](PLATFORMS.md) — proot Debian is
  supported, native Termux is experimental until the on-device acceptance script passes.

---

## 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

- Debian 12 (x86_64 or arm64), kernel ≥ 5.x
- 2 vCPU, 2 GB RAM, 20 GB disk (the tree is ~490 MB, media included)
- root / passwordless sudo
- A domain or DuckDNS subdomain with an **A record pointing at the box** (do this first)
- Ports 22 (or your SSH port), 80, 443 reachable
- Outbound HTTPS for certbot

## 3. Quickstart

```bash
# 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 →
6. users DB → 7. systemd units → 8. HTTP bootstrap vhost → certbot → real vhost →
9. 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.

| Port | Service | Port | Service |
|---|---|---|---|
| 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:

```bash
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

```bash
# 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

```bash
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`](UPGRADING.md).

## 10. Uninstall

```bash
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

- **Clone size is ~490 MB** (277 MB worktree + 212 MB git). ~120 MB of that is site media
  (`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.
- 240+ archived pages still link to `webx11.duckdns.org`. `deploy/rebrand.py --from … --to …`
  rewrites live pages (dry-run by default; skips `archive/`, `_data/memory/`, `.git`).
- There are **no cross-node accounts, shared payments or content replication**. Federation is
  discovery + search only. See the non-goals in `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).
- On hosts without systemd (containers, Termux) services run under `deploy/lib/supervisor.py`
  instead of units. Same `services.tsv`, same templates — only the process parent changes.
