Pular para o conteúdo
Decoding Chat API

Primeiros passos

Ao final você terá uma instância do Decoding Chat em um hostname seu, rodando o app web, a API HTTP, o Gateway, o painel de administração, uploads, busca e sinalização de voz em contêineres Docker em uma única máquina. Reserve cerca de 30 minutos, a maior parte gasta baixando imagens.

RequisitoValor
HostQualquer máquina que rode Docker com contêineres Linux. Um servidor Linux, ou um Mac ou PC Windows com Docker Desktop
RuntimeDocker Engine 24 ou mais novo, ou Podman 5 ou mais novo, com o plugin Compose v2.20.2 ou mais novo
ProcessadorIntel, AMD e Apple Silicon funcionam. Toda imagem é publicada para amd64 e arm64, e o Docker baixa a correspondente
CPU2 vCPU no mínimo, 4 vCPU para uma comunidade pequena e ativa. Nenhum serviço define limite de CPU, então todo contêiner enxerga todas
RAM8 GB no mínimo com os padrões de fábrica, 16 GB para uma comunidade pequena e ativa. Com 4 GB são necessários os ajustes de pouca memória
Disco20 GB no mínimo, mais que isso para uploads
Portas de entrada80/tcp, 443/tcp e 443/udp para a camada web, mais 7881/tcp e 7882/udp para a mídia de voz
DNSUm registro A do hostname apontando para esta máquina, e opcionalmente um AAAA
SaídaResolução de DNS funcionando de dentro dos contêineres
Ferramentas no hostcurl e openssl no Linux. O macOS já traz os dois. O instalador do Windows não precisa de nenhum

Limites de memória são tetos, não reservas. Em um host de 4 GB, reduza primeiro os limites de api e worker. Com Docker Desktop, verifique a RAM atribuída à máquina virtual Linux dele, e não o total do host.

A stack é um único projeto Compose. A borda e o LiveKit são os únicos serviços que publicam portas.

Os passos abaixo usam o Caddy que vem junto, que obtém e renova o certificado TLS do hostname e não exige configuração. Para colocar seu próprio proxy na frente, adicione o overlay docker-compose.proxy.yml. A stack então deixa a 80 e a 443 em paz e publica uma porta HTTP simples para o seu proxy encaminhar. Atrás do seu próprio proxy reverso traz o overlay, os requisitos e configurações prontas para nginx, Caddy, Traefik, HAProxy, Apache, Cloudflare Tunnel e Nginx Proxy Manager.

Veja o que você já tem:

Janela do terminal
docker --version
docker compose version

Se os dois responderem, pule adiante. Em um Mac ou PC Windows, instale o Docker Desktop e deixe-o em contêineres Linux, o que já entrega os dois.

Debian e Ubuntu precisam do repositório apt do Docker, descrito em Install Docker Engine, porque o pacote docker.io da própria distribuição não traz o plugin Compose. Siga os passos pós-instalação para adicionar seu usuário ao grupo docker, depois saia e entre de novo na sessão para que os comandos abaixo rodem sem sudo.

O Linux também precisa de curl e openssl, usados nos passos seguintes:

Janela do terminal
sudo apt-get install -y curl openssl

Troque apt-get por dnf, zypper, pacman ou apk conforme a distribuição. Os nomes dos pacotes são os mesmos, e curl --version e openssl version respondem assim que a instalação termina.

Escolha o hostname em que a instância vai responder. O Passo 4 o escreve no .env como FLUXER_DOMAIN, e todos os exemplos abaixo usam chat.example.com no lugar dele.

Crie um registro A desse hostname apontando para o endereço público da máquina, e um AAAA quando a máquina tiver endereço IPv6 público. Um provedor de hospedagem mostra esse endereço no painel. Em uma conexão residencial, o endereço pertence ao roteador, e a página de status dele mostra qual é.

Abra estas portas no host:

PortaProtocoloUsada por
22tcpSSH, em um host que você acessa remotamente
80tcpValidação HTTP do ACME e redirecionamento para HTTPS
443tcpO app web, a API HTTP, o Gateway e o painel de administração
443udpHTTP/3 para o mesmo tráfego
7881tcpMídia do LiveKit, publicada pelo contêiner do LiveKit
7882udpMídia do LiveKit, publicada pelo contêiner do LiveKit

Uma máquina atrás de roteador residencial precisa da 80, da 443 e das duas portas do LiveKit encaminhadas para ela. O certificado é emitido a partir da internet pública, e a mídia de voz chega direto nas portas do LiveKit. Rodar seu próprio proxy reverso muda apenas as duas primeiras. O proxy fica com elas, e a stack escuta uma porta HTTP simples no loopback. A 7881 e a 7882 precisam alcançar o host de qualquer jeito, porque mídia de voz nunca passa por proxy.

Para usar outra porta, abra ela no lugar da 443 e leia Servindo em outra porta.

Em uma VM na nuvem, abra as portas no firewall ou no grupo de segurança do provedor. Em um host Linux com firewalld:

Janela do terminal
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-port=80/tcp --add-port=443/tcp --add-port=443/udp \
--add-port=7881/tcp --add-port=7882/udp
sudo firewall-cmd --reload

O instalador baixa os arquivos da stack, escreve o .env, gera todos os segredos e inicia a instância. Baixe, confira o SHA-256 publicado, leia e só então execute.

No Linux:

Janela do terminal
curl -fsSLO https://docs.example.com/install.sh
curl -fsSLO https://docs.example.com/install.sh.sha256
sha256sum -c install.sh.sha256

No macOS, os mesmos downloads e:

Janela do terminal
shasum -a 256 -c install.sh.sha256

No Windows:

Janela do terminal
Invoke-WebRequest https://docs.example.com/install.ps1 -OutFile install.ps1
Invoke-WebRequest https://docs.example.com/install.ps1.sha256 -OutFile install.ps1.sha256
(Get-FileHash .\install.ps1 -Algorithm SHA256).Hash -eq (Get-Content .\install.ps1.sha256).Split()[0]

install.sh: OK no Linux e no macOS, e True no Windows, significam que o arquivo chegou íntegro. O digest da cópia que este site serve é 4e7f16b7ac5d17dd94ca5197f2faa6800f283cd9ab9fe8f011a0716010eee2e3 para o install.sh e 5bcb68326f2efdb65e09a676d72843ad3a94dae119ba76c7221aca31da1c7614 para o install.ps1.

Leia o script antes de rodar. Ele escreve o .env, e esse arquivo guarda todos os segredos da instância.

A execução abaixo usa o TLS que vem junto. Acrescente --tls proxy quando outra coisa terminar o TLS, e leia Atrás do seu próprio proxy reverso antes.

Então execute, com o hostname do Passo 2. O --email vira o endereço de contato no web push de saída:

Janela do terminal
sh install.sh --domain chat.example.com --email voce@example.com

No Windows:

Janela do terminal
powershell -ExecutionPolicy Bypass -File .\install.ps1 -Domain chat.example.com -Email voce@example.com

A execução imprime uma linha por fase e termina com a URL para abrir.

O diretório ~/fluxer passa a conter docker-compose.yml, docker-compose.proxy.yml, tunnel.compose.yml, Caddyfile e .env.example, além de um .env legível apenas por você. Todo valor que vem como CHANGE_ME no .env.example recebe um valor aleatório novo. Todos os comandos daqui em diante rodam nesse diretório.

O instalador escreve um .env para https na 443 e não tem parâmetro para outra porta. As duas linhas abaixo no .env movem a instância para outra porta. FLUXER_PUBLIC_PORT define a porta do endereço público, e FLUXER_HTTPS_PORT define a porta do host em que a borda publica o HTTPS:

FLUXER_PUBLIC_PORT=8443
FLUXER_HTTPS_PORT=8443
Janela do terminal
docker compose up -d

FLUXER_PUBLIC_PORT é a porta do endereço público, e todo endpoint que os serviços anunciam acompanha esse valor. Ele não move o que o host publica, então FLUXER_HTTPS_PORT precisa apontar para a mesma porta. Mova o FLUXER_PUBLIC_SCHEME junto quando a nova porta servir HTTP simples, e defina FLUXER_HTTP_PORT no lugar de FLUXER_HTTPS_PORT. O .env.example fica ao lado do .env e traz as duas receitas completas.

A porta 80 do host continua publicada na receita https e segue respondendo ao desafio ACME. A Let’s Encrypt só se conecta à 80 ou à 443 públicas, então o certificado é emitido se um roteador na frente encaminhar a 80 pública para este host. Sirva seu próprio certificado pelo Caddyfile quando isso não for possível.

Deixe o FLUXER_PUBLIC_ORIGIN comentado, ou mantenha-o coerente com o esquema, o domínio e a porta públicos. Endereços conflitantes podem causar falhas de login e travar a configuração em loop. Veja A origem pública.

ParâmetroSignificado
--domain <host>Hostname em que a instância responde. É perguntado quando ausente
--email <endereço>Endereço de contato escrito como FLUXER_VAPID_EMAIL. É perguntado quando ausente
--dir <caminho>Diretório de trabalho. Padrão ~/fluxer, ou o diretório atual quando ele já contém uma instância
--ref <git ref>Ref de onde vêm os arquivos da stack. Assume a tag da imagem, e main quando a tag é v1 ou latest
--image-tag <tag>Tag de imagem que a stack roda. Padrão v1
--tls <modo>bundled ou proxy. Padrão bundled
--edge-bind <addr:porta>Bind HTTP simples sob --tls proxy. Padrão 127.0.0.1:8080
--non-interactiveNunca pergunta. Um valor obrigatório ausente vira erro
--dry-runImprime o plano. Não escreve nada e não inicia nada
--no-startEscreve tudo e pula o docker compose up -d
--updateAtualização: registra, faz backup, atualiza os arquivos, baixa, recria e verifica
--rollbackRestaura as imagens e os arquivos de stack que a execução --update mais recente registrou
--allow-rootPermite rodar como root
--engine <comando>Motor de contêiner a usar. Padrão docker, ou podman quando o docker não existe

Rode com --dry-run primeiro para ver o que um conjunto de parâmetros faz. O script PowerShell recebe os mesmos parâmetros como -Domain, -Email e assim por diante, e não tem -AllowRoot. Atualização traz os parâmetros que só --update e --rollback leem, e os códigos de saída.

Leia o instalador de Linux e macOS ou o instalador de Windows, servidos por este site em /install.sh e /install.ps1, antes de executar.

Acompanhe a stack se estabilizar:

Janela do terminal
cd ~/fluxer
docker compose ps
docker compose logs -f api

Todo serviço mostra running ou healthy, exceto o seaweedfs-init, que cria os buckets de upload e depois mostra exited (0).

Em seguida, as sondagens públicas. Troque a origem pela sua, com a porta incluída quando a instância responder em uma:

Janela do terminal
base=https://chat.example.com
for p in /_health /api/_health /gateway/_health /media/_health /.well-known/fluxer /; do
printf '%s %s\n' "$(curl -sS -o /dev/null -w '%{http_code}' "$base$p")" "$p"
done

No Windows:

Janela do terminal
$base = 'https://chat.example.com'
foreach ($p in '/_health', '/api/_health', '/gateway/_health', '/media/_health', '/.well-known/fluxer', '/') {
'{0} {1}' -f (Invoke-WebRequest "$base$p" -UseBasicParsing).StatusCode, $p
}

Todos os caminhos retornam 200. O /.well-known/fluxer lista os endpoints que os clientes usam, já com o seu hostname neles.

Abra o hostname no navegador. Uma instância nova não está configurada, então ela serve o assistente de configuração.

Crie uma conta e depois escolha identidade visual, modo de registro, política da comunidade, expiração de mídia, integrações e ajustes de premium. Concluir o assistente fecha o acesso de configuração.

Crie a conta de dono com um endereço de e-mail em um domínio que você controle. O primeiro registro que incluir um endereço de e-mail recebe a ACL curinga de administração, que é acesso total ao painel, a não ser que o registro esteja configurado para reter novas contas para aprovação. Concluir o assistente concede essa mesma ACL à conta que o completou, quando essa conta não tem nenhuma.

Com o e-mail ligado, o endereço passa por uma checagem de DNS antes de a conta existir. O e-mail conta como ligado quando a chave está ativa e o provedor é smtp com uma configuração SMTP completa, então FLUXER_EMAIL_ENABLED=true sozinho não liga a checagem. O domínio do endereço precisa publicar um registro MX, ou um A ou AAAA como alternativa. Um nome que só resolve na sua própria rede, como um .lan, .internal ou home.arpa, não publica nenhum dos dois, e o formulário de conta responde That email domain cannot receive mail. qualquer que seja o endereço. Use um domínio com registros públicos, ou deixe o e-mail desligado.

O .env.example vem com FLUXER_EMAIL_ENABLED=false, o que marca todo endereço como verificado na criação e não envia mensagem alguma. Uma senha de dono esquecida, portanto, não tem redefinição por e-mail. Anote-a, e cadastre uma passkey ou uma segunda conta de administrador antes de abrir o registro.

Depois entre no painel de administração em https://chat.example.com/admin com a conta que tem a ACL curinga. A página Instance Config traz tudo o que o assistente perguntou, mais modo de registro, aprovações e chaves de integração. Limit Config guarda os limites da instância publicados aos clientes. Voice Regions e Voice Servers já vêm preenchidas, então voz não precisa de configuração ali.

O cliente desktop abre o app web hospedado do canal de release dele, então acesse sua própria instância pelo navegador. A issue #1088 do projeto de origem acompanha esse ponto e está em andamento.

Configuração lista todos os ajustes de runtime e a variável de ambiente que sobrescreve cada um.

A recuperação exige o .env, um dump do banco e um backup de seaweedfs-data, que guarda uploads, avatares, denúncias e coletas de dados. Veja Volumes e buckets para tudo o que precisa de backup.

O dump não custa indisponibilidade, então rode-o de forma agendada com a stack no ar:

Janela do terminal
cd ~/fluxer
mkdir -p backups
docker compose exec -T postgres pg_dump -U fluxer -d fluxer --format=custom \
> "backups/fluxer-$(date -u +%Y%m%dT%H%M%SZ).dump"

O PowerShell escreve UTF-16 com >, o que corrompe um dump binário. No Windows, escreva o dump dentro do contêiner e copie para fora:

Janela do terminal
mkdir -Force backups
docker compose exec -T postgres pg_dump -U fluxer -d fluxer --format=custom -f /tmp/fluxer.dump
docker compose cp postgres:/tmp/fluxer.dump backups/fluxer.dump
docker compose exec -T postgres rm /tmp/fluxer.dump

A cópia dos uploads para a stack, porque uma cópia com o serviço no ar pode pegar um arquivo em meio à escrita. O Compose prefixa os nomes de volume com o nome do projeto, que a stack define como fluxer:

Janela do terminal
docker compose stop
docker run --rm -v fluxer_seaweedfs-data:/data -v "$PWD/backups:/backup" alpine:3.22 \
tar czf /backup/seaweedfs-data.tgz -C /data .
docker compose up -d

No PowerShell, escreva ${PWD} no lugar de $PWD.

Você terminou quando backups contiver um dump e um tarball e todo serviço voltar a mostrar running.

O sh install.sh --update faz os dois backups antes de atualizar. Veja O que o backup cobre para backups agendados e Restaurar um backup para recuperação.

Derrube a instância e apague os dados dela:

Janela do terminal
docker compose down -v

A tag padrão v1 acompanha a release compatível mais recente. O mesmo script atualiza a instância:

Janela do terminal
cd ~/fluxer
sh install.sh --update

O --update registra as imagens em execução, faz backup do banco e dos uploads, atualiza os arquivos da stack, baixa as imagens, recria os contêineres e verifica. Ele não toca em nenhum segredo do .env. No Windows é .\install.ps1 -Update. Rode sh install.sh --update --dry-run primeiro para ver o plano.

Atualização cobre o que o backup guarda, como reverter, como fixar uma release e como recuperar espaço em disco.

Abra issues e acompanhe o desenvolvimento no GitHub.