# Arquitetura
## Visão geral
```mermaid
flowchart TB
subgraph Internet
B[Browser
Chrome/Edge/Firefox]
end
subgraph Host["Host / VPS (Docker)"]
T[Traefik
:80 / :443]
W[web-client
nginx + build Babylon]
P[ws-proxy
Bun WS↔TCP]
DOC[docs
nginx]
subgraph O["openmu (1 processo all-in-one)"]
CS1[ConnectServer :44405
cliente original, sem uso]
CS2[ConnectServer :44406
protocolo estendido]
GS[GameServers 0..2
:55901-55906]
LS[Login / Friend / Guild
in-process]
CH[ChatServer :55980]
AP[Admin Panel + /api :8080]
end
PG[(PostgreSQL 18
rede interna)]
end
B -- HTTPS --> T
B -- WSS --> T
T -- play.* --> W
T -- ws.* --> P
T -- admin.* --> AP
T -- docs.* --> DOC
P -- TCP 44406 --> CS2
P -- TCP 55902/04/06 --> GS
GS --- LS
O -- backend (internal) --> PG
```
### Redes Docker
| Rede | Membros | Observação |
|---|---|---|
| `edge` | reverse-proxy, web-client, ws-proxy, docs, openmu | publica só 80/443 (Traefik); o openmu não publica nenhuma porta do jogo (D15) |
| `backend` (`internal: true`) | postgres, openmu | sem acesso externo; PostgreSQL **nunca** é publicado |
## Componentes
| Componente | Imagem/origem | Papel |
|---|---|---|
| **Browser** | — | Baixa o cliente Babylon de `play.DOMAIN`, abre WebSocket em `ws.DOMAIN`. |
| **ws-proxy** | `services/game-proxy` (Bun, código `clients/web/proxy`) | Recebe `ws://…/?host=H&port=P`, valida contra `ALLOW_TARGETS`, abre TCP e repassa bytes. Também tem tracker/journal (SQLite em volume), relay de "band"/"ping" e clima sintético. Não termina TLS (o Traefik termina). |
| **ConnectServer** | OpenMU | Lista de servidores e endereço do GameServer. Um por tipo de cliente (44405 original, sem uso; 44406 protocolo estendido, usado pelo web). |
| **GameServer** | OpenMU | Mundo do jogo. Cada servidor tem um endpoint por tipo de cliente. |
| **LoginServer / FriendServer / GuildServer** | OpenMU | No all-in-one rodam no mesmo processo (não há portas próprias). |
| **ChatServer** | OpenMU :55980 | Messenger in-game (salas de chat). |
| **Admin Panel** | OpenMU :8080 (Blazor) | Setup, contas, configuração, plugins, mapa ao vivo, logs, API `/api` (API keys). Login próprio com bootstrap via `OPENMU_ADMIN_*`. |
| **PostgreSQL 18** | `postgres:18.6-alpine` | Schemas `config`, `data`, `friend`, `guild`, `admin`… O OpenMU usa o superusuário para criar banco e roles na 1ª inicialização. |
| **Traefik v3** | `traefik:v3.7.13` | Roteamento por host, TLS Let's Encrypt (prod). Provider de **arquivo** (sem `docker.sock`). |
| **docs** | `nginx:1.30.5-alpine` | Serve `docs/` (esta documentação + `index.html`). |
## Fluxos
### Browser → WSS → Proxy → OpenMU
1. `GET https://play.DOMAIN/` → Traefik → nginx (bundle estático; `VITE_*` definidos no build).
2. JS abre `wss://ws.DOMAIN:443/?host=openmu&port=44406&session=` → Traefik (TLS) → ws-proxy:3000.
3. Proxy confere `openmu:44406` em `ALLOW_TARGETS` (senão **403**) e abre TCP na rede docker.
4. CS devolve `127.127.127.127:55902` (valor fixo, ignorado); o cliente reconecta em `?host=openmu&port=55902` (política "auto"/`gs=cs`).
5. Login (`LoginLongPassword`, versão 20404, SimpleModulus/Xor32), pacotes `*Extended`, seleção de personagem, jogo.
O cliente desktop foi removido do projeto (D15).
### Dentro do OpenMU
```
ConnectServer ──(registro de endpoints)── GameServer
│ PlayerActions / PacketHandlers (plugins)
▼
GameLogic ── PlugInManager (pontos de extensão, nossos MuCustom.*)
│
▼
Persistence (EF Core, contexts por role)
│
▼
PostgreSQL
```
## Decisões de arquitetura (resumo — detalhes em [decisions.md](decisions.md))
1. **All-in-one + Traefik**, não o deployment distribuído (documentado pelo upstream como quebrado).
2. **Upstreams como git submodules** fixados por SHA; nada de `latest` implícito.
3. **Camada custom do servidor via `DOTNET_STARTUP_HOOKS`** → zero patches no core.
4. **Cliente web em build de produção** (nginx), não o dev server do compose upstream.
5. **Traefik sem docker.sock** (provider de arquivo com templates) — menos superfície.
6. **Proxy WS com allowlist obrigatória** — o padrão upstream é relay aberto.
7. **Web no protocolo estendido** (CS 44406, GS 55902/04/06) — D16; nenhuma porta do jogo publicada (D15).