Por padrão quem cria a sala é o servidor (e precisa de túnel/porta aberta). Se
você tem uma VPS ou um PC com porta aberta, pode deixar uma sala sempre no ar
com o telinha-server: um binário estático de ~400 KB, sem dependências.
Todo mundo entra com Entrar numa sala → ip-da-vps:7777 + senha.
Rodando direto
zig build server # gera zig-out/bin/telinha-server-{x86_64,aarch64}
./telinha-server-x86_64 --porta 7777 --senha "uma senha boa" --nome "Sala da galera" --admin "token-secreto"
Ou pelas variáveis TELINHA_PORTA, TELINHA_SENHA, TELINHA_NOME, TELINHA_ADMIN.
O --admin é um token: quem entrar com ele (no app: Entrar numa sala →
Opções avançadas → token de admin, ou --admin TOKEN na linha de comando)
ganha a coroa e pode expulsar gente (clique direito no membro).
Docker
docker build -t telinha-server .
docker run -d --name telinha -v telinha-dados:/data -p 7777:7777/tcp -p 7777:7777/udp \
-e TELINHA_SENHA="uma senha boa" -e TELINHA_NOME="Sala da galera" -e TELINHA_ADMIN="token-secreto" \
--restart unless-stopped telinha-server
Ou com docker compose up -d usando o docker-compose.yml:
copie o .env.example pra .env e preencha a senha e o token de
admin antes (sem eles o compose não sobe).
A imagem final é FROM scratch (só o binário), roda sem root e funciona em amd64 e
arm64. Mantenha o volume /data: nele ficam a identidade do servidor
(servidor.key), o estado da sala e os arquivos; sem ele cada restart cria uma
identidade nova e o app de todo mundo avisa que a identidade do servidor mudou.
systemd
/etc/systemd/system/telinha.service:
[Unit]
Description=telinha - servidor de sala
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/usr/local/bin/telinha-server --porta 7777 --nome "Sala da galera"
Environment=TELINHA_SENHA=uma-senha-boa
Environment=TELINHA_ADMIN=token-secreto
# os logs de depuração que mandarem "pro dono da sala" também ficam aqui
Environment=TELINHA_LOGS=/var/lib/telinha/logs
StateDirectory=telinha
DynamicUser=yes
Restart=on-failure
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
[Install]
WantedBy=multi-user.target
sudo install -m755 zig-out/bin/telinha-server-x86_64 /usr/local/bin/telinha-server
sudo systemctl enable --now telinha
journalctl -u telinha -f
Atualizar sozinho
Com --atualizar-sozinho (ou TELINHA_ATUALIZAR=1) o servidor confere a release
nova a cada 6 h, baixa o telinha-server dela, confere a assinatura e a versão
assinada (o mesmo do app), e só reinicia quando a sala fica 2 minutos vazia
(bots não contam). Reinicia no mesmo processo, então o systemd e o Docker nem
percebem; se a versão nova cair nos primeiros 10 s, a próxima abertura volta pra
anterior sozinha.
O executável precisa estar numa pasta em que o serviço pode escrever. Com a unit
acima (ProtectSystem=strict), use a pasta de estado:
sudo install -D -m755 zig-out/bin/telinha-server-x86_64 /var/lib/telinha/bin/telinha-server
# e na unit: ExecStart=/var/lib/telinha/bin/telinha-server --atualizar-sozinho ...
Sem poder escrever, o servidor só avisa no log que tem versão nova. No Docker, atualize a imagem (o binário do container é só leitura).
Logs de depuração da galera
No app, Configurações → Depuração → Pro dono da sala manda o log pela própria
sala. Ele chega em todo mundo que entrou com o token de admin (vai pra pasta
logs-recebidos, do lado da configuração). Com --logs PASTA (ou
TELINHA_LOGS), o servidor dedicado também guarda uma cópia: um log mandado com
nenhum admin na sala não se perde. Cada pessoa manda um log por minuto, até 8 MB.
Com --coletar-logs (ou TELINHA_COLETAR_LOGS=1) não precisa pedir pra
ninguém: quem entra é avisado, o app grava o log enquanto a pessoa está na sala
e quem tem a coroa clica com o botão direito na pessoa → Pegar o log. O
relatório de travamento de quem caiu chega sozinho. Quem não quiser desliga em
Configurações → Depuração (e você fica sabendo que não veio). Sem --logs, os
logs ficam em logs, do lado do arquivo de estado.
Quando o servidor cai
Se o servidor cair (um bug) e o systemd o reabrir, ele escreve no chat do canal
padrão "O servidor reiniciou depois de uma queda (motivo: …)", com o arquivo e
a linha, e deixa o detalhe em ultima-queda.txt, do lado do arquivo de estado
(a pilha inteira também vai pro journal).
Canais
Um servidor só pode ter várias salas ao mesmo tempo, como no IRC: cada canal com
o seu chat, a sua gente, a sua voz e as suas transmissões, e a galera pula de um
pro outro sem reconectar (o painel da direita, ou /join nome no chat). O
primeiro canal é o padrão.
telinha-server --canais "geral,jogos,filmes" # canais fixos; qualquer um cria outros
telinha-server --canais "geral,jogos" --sem-canais-novos # só esses
(ou TELINHA_CANAIS). Canal que alguém criou some quando esvazia. O convite pode
apontar direto pra um canal: telinha://host:7777/jogos#senha.
Admin dos canais, calls e o que sobrevive a reiniciar
Cada canal tem o seu chat; a call (voz e transmissões) é separada: dá pra
ler um canal e continuar na call de outro (o fone em cada canal, /call nome,
ou o botão da cápsula). Quem entrou com o token de admin clica com o botão
direito num canal pra fixar, trancar (só admin entra), pôr senha ou
tópico, e numa pessoa pra levar ela pra outro canal. Canais fixos (com senha,
trava e tópico), banidos, silenciados e sem pincel vão pra um arquivo de estado
(--estado ARQUIVO / TELINHA_ESTADO; por padrão estado.txt do lado da chave) e
voltam depois de reiniciar.
Assistir pelo navegador
Quem não tem o app assiste por um link, sem instalar nada. O servidor serve a
página e repassa os mesmos quadros que já manda pros apps (quem transmite não
gasta nada a mais). O navegador só decodifica vídeo em página https, então a
porta web fica em 127.0.0.1 e precisa de um HTTPS na frente:
- Sem conta, sem domínio:
--web 7778 --web-tunelsobe um túnel rápido da Cloudflare (cloudflared, baixe aqui) e divulga o endereçohttps://….trycloudflare.comna sala; no app, o botão da telinha (ao lado do convite) copia o link com a senha. O endereço muda a cada vez que o túnel sobe — o app sempre recebe o atual. - Com domínio seu (túnel nomeado da Cloudflare, Caddy, nginx...): aponte o
HTTPS pra
http://127.0.0.1:7778(com WebSocket) e use--web 7778 --web-link https://tela.seudominio.com.
No systemd, junte às opções do ExecStart (ex.: --web 7778 --web-tunel) com o
cloudflared em /usr/local/bin.
Bots
Bots (o SDK em Python, o bot de música) entram com a senha da sala e aparecem com a etiqueta BOT; nunca viram admin. Pra decidir quem entra:
--sem-bots(TELINHA_SEM_BOTS=1): recusa todos os bots.--so-protocolo-novo(TELINHA_SO_PROTOCOLO_NOVO=1): recusa apps de antes da 3.8 (protocolo 6). No protocolo 6, quem alcança a porta consegue, com uma conexão, algo pra testar a senha da sala offline; ligue quando todo mundo tiver atualizado (o app se atualiza sozinho; os bots precisam do SDK da 3.8 em diante).--token-bots SEGREDO(TELINHA_TOKEN_BOTS): só bot que mandar esse token (Bot(..., bot_token="SEGREDO"),--token-botsno bot de música). Gente comum continua entrando só com a senha.
Arquivos
Quem está na sala manda arquivos (até 256 MB) e cada um baixa quando quiser. O
servidor guarda no disco, cifrados com uma chave que só existe na memória dele
(reiniciou, o que sobrou é apagado), em --arquivos PASTA / TELINHA_ARQUIVOS
(por padrão a pasta arquivos do lado do estado; no systemd acima,
/var/lib/telinha/arquivos). Expiram 2 h depois de mandados, no máximo 2 GB no
total. Sem uma pasta que dê pra escrever, a sala fica sem arquivos (as imagens
no chat continuam).
Identidade do servidor
O servidor guarda uma chave fixa (--chave ARQUIVO / TELINHA_CHAVE; por padrão
servidor.key no StateDirectory= do systemd,
/var/lib/telinha/servidor.key rodando como root, senão
~/.config/telinha/servidor.key), criada na primeira vez com permissão 0600. A
impressão aparece no log ao subir e vai no convite
(telinha://host:7777!impressão#senha): o app confere que é mesmo este servidor
antes de mandar qualquer coisa. Guarde o arquivo da chave ao reinstalar — chave
nova faz quem já entrou ver o aviso "a identidade do servidor mudou".
Estatísticas do servidor
Quem entra com o token de admin (--admin) ganha um botão de medidor no topo da
sala: CPU e memória do processo do servidor, banda chegando e saindo, gente por
canal, UDP ou TCP, quanto cada um está recebendo, a fila de envio de cada um, e um
palpite em português claro de qual é o gargalo (CPU do servidor, upload do
servidor, a conexão de uma pessoa). O servidor só mede enquanto alguém está com
essa janela aberta.
Firewall e banda
- Libere a porta escolhida em TCP e UDP (
ufw allow 7777). O TCP leva o controle e o chat; o UDP, na mesma porta, leva vídeo, som e voz. Sem o UDP funciona, mas tudo vai pelo TCP (perda de pacote segura o que vem atrás). - Na nuvem (Oracle, AWS, GCP...) tem também o firewall da rede, fora da máquina (security list / security group): libere TCP e UDP 7777 lá também.
- Nas imagens Ubuntu da Oracle, o iptables rejeita tudo antes do UFW; a regra vai
antes do REJECT:
sudo iptables -I INPUT <linha do REJECT> -p udp --dport 7777 -j ACCEPT(e o mesmo com-p tcp), salvando em/etc/iptables/rules.v4. - O servidor só encaminha: quase não usa CPU. A banda de saída é (soma das transmissões) × (espectadores de cada uma). Uma transmissão 1080p "equilibrada" gira em 0,3–6 Mbit/s dependendo do que está na tela.
Sem VPS: link público
Se quem cria a sala está atrás de NAT/CGNAT, o próprio telinha abre um túnel
(protocolo do bore, servidor bore.pub):
no app, Criar link público; no servidor dedicado, --publico:
./telinha-server --publico
# [21:03] link público: bore.pub:41234 — convite: telinha://bore.pub:41234#k3m9qx2a
Sem --senha, uma senha aleatória é criada (o endereço é público). Se o túnel
cair, ele reconecta pedindo a mesma porta, então o convite continua valendo.
Outro servidor bore (o seu, por exemplo): --publico meu.servidor.com (ou
tunnel_host= no config.ini do app).
Sem VPS: porta aberta no roteador (UPnP)
Com o roteador de casa deixando (a maioria vem com UPnP ligado), dá pra ter
conexão direta, sem túnel no meio: menos atraso (o bore.pub fica em outro
continente) e sem o limite de banda dele. No app, Abrir porta no roteador; no
servidor dedicado, --upnp:
./telinha-server --upnp
# [21:03] porta aberta no roteador: 177.45.10.8:7777 — convite: telinha://177.45.10.8:7777#k3m9qx2a
A porta fica aberta (renovada a cada 25 min) enquanto a sala está aberta e é fechada quando ela fecha; se o processo cair sem fechar, o roteador apaga sozinho em até 1 h. Se a operadora usa CGNAT (o roteador não tem IP próprio na internet — comum em fibra e 4G), o telinha percebe e avisa: aí o caminho é o link público. O firewall da própria máquina também precisa deixar a porta entrar.
Ou um túnel externo:
| túnel | comando | observação |
|---|---|---|
| bore | bore local 7777 --to bore.pub |
grátis, sem conta |
| ngrok | ngrok tcp 7777 |
TCP exige conta com cartão verificado |
| serveo | ssh -R 0:localhost:7777 serveo.net |
pode estar instável |
| playit.gg | app do playit | grátis, feito pra jogos |
| Tailscale/ZeroTier/Radmin | VPN entre a galera | usa o IP da VPN direto |
O tráfego é criptografado de ponta a ponta entre cada pessoa e o servidor da sala, então o túnel não vê o conteúdo.
