feat: acesso remoto documentado e cada gateway na sua porta no host - #8
Conversation
**Portas.** O claudegravity dos artigos ocupa a 20128, que e a porta padrao do 9Router. As stacks dos repositorios saem dessa faixa para que tudo rode junto: 9Router em 8081, OmniRoute em 8082, LiteLLM em 8083; os paineis seguem em 9091, 9092 e 9093. Os exemplos de compose embutidos na documentacao foram sincronizados -- um exemplo que diverge do arquivo real e pior que exemplo nenhum, porque o leitor copia e fica com duas verdades. Novo teste: toda porta citada na documentacao tem de existir num compose deste repositorio. Verificado introduzindo 127.0.0.1:9999 numa copia. **Acesso remoto.** Pagina nova na wiki, Remote-Access, cobrindo tunel Cloudflare, Tailscale e proxy reverso -- com a ordem que importa: 1. exigir login e definir senha propria 2. exigir chave de API 3. so entao expor A ordem nao e preferencia. As stacks sobem com REQUIRE_LOGIN=false e REQUIRE_API_KEY=false, o que e seguro enquanto a porta esta presa em 127.0.0.1 -- e deixa de ser no instante em que o gateway ganha um endereco publico: - `/v1` e prefixo publico por projeto no 9Router (src/dashboardGuard.js), para que as ferramentas de codigo chamem o gateway sem sessao. Com chave de API desligada e URL publica, quem descobrir o endereco gasta as contas; - as rotas administrativas do gateway so sao protegidas quando requireLogin esta ligado. Com ele desligado, um gateway exposto entrega a propria configuracao; - o bloqueio do botao de tunel enquanto o login esta desligado vive na tela: o endpoint nao reavalia a condicao. A pagina tambem separa duas coisas que o Tailscale faz e que se confundem: ENTRAR (alcancar o gateway de fora, aqui) e SAIR (cada conta pelo seu endereco, em Egress-And-Multi-Session). Versoes proprias para OmniRoute e LiteLLM, que nao tem botao nativo: o caminho e cloudflared ou tailscale a mao, e no LiteLLM a protecao e a master key, nao uma flag de login.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 388862f3ed
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # Cada gateway tem a sua porta no host -- 8081 9Router, 8082 OmniRoute, | ||
| # 8083 LiteLLM -- para que os tres rodem juntos, inclusive ao lado da | ||
| # stack do artigo, que fica com a 20128. | ||
| - "127.0.0.1:8082:20128" |
There was a problem hiding this comment.
Update the public base URL with the port mapping
When users run docker-compose.example.yml, the gateway is now reachable from the browser only on host port 8082, but NEXT_PUBLIC_BASE_URL remains http://localhost:20128. Any browser-facing URLs or redirects derived from that variable will target a port where this compose publishes nothing; update it to 8082 alongside the mapping, as the README example already does.
Useful? React with 👍 / 👎.
| contexto = "".join(linhas[max(0, n - 3):n + 1]) | ||
| if RX_ROTA_DO_GATEWAY.search(contexto): | ||
| continue |
There was a problem hiding this comment.
Limit gateway-route context to the actual paragraph
The fixed four-line window now exempts unrelated project routes whenever a gateway term happens to occur nearby. For example, README line 262's panel route POST /api/sync is silently classified as a gateway route solely because OmniRoute appears two lines earlier, so removing or renaming /api/sync would no longer produce the documentation divergence this validator is meant to catch. Bound the context by paragraph or otherwise associate the gateway qualifier with the route more precisely.
Useful? React with 👍 / 👎.
| The panel of this synchronizer has no reason to leave the machine: it reads the | ||
| gateway's database and shows credentials' health. Keep its port on `127.0.0.1` | ||
| and reach it through the same tunnel or tailnet you use for everything else. |
There was a problem hiding this comment.
Provide a route to the loopback-bound panel
When a user follows either remote-access setup above, only gateway port 8082 is exposed: the Cloudflare command targets that port, while the Tailscale example rebinds only that mapping. The panel remains published solely on 127.0.0.1:9092, which neither a remote tailnet client nor that quick tunnel can reach, so the instruction to access it through the same tunnel or tailnet is nonfunctional. Document a separate tunnel/SSH/Tailscale Serve route or a tailnet-only panel binding.
Useful? React with 👍 / 👎.
| curl -si https://<your-address>/v1/models | head -1 # expect 401 | ||
| curl -si https://<your-address>/ | head -1 # expect 401 or a login page |
There was a problem hiding this comment.
Use HTTP when checking the Tailscale endpoint
For users following Option 2, the endpoint created above is explicitly plain http://100.x.y.z:8082, with no TLS terminator. These unconditional https:// probes therefore fail during TLS negotiation instead of returning the expected 401 or login response, which can make a transport failure look like successful authentication protection. Provide an HTTP check for Tailscale and keep HTTPS for the Cloudflare/reverse-proxy cases.
Useful? React with 👍 / 👎.
| ``` | ||
| 1. REQUIRE_LOGIN=true + a password you chose | ||
| 2. REQUIRE_API_KEY=true + a key for your tools | ||
| 3. only then, the tunnel or Tailscale |
There was a problem hiding this comment.
Wire the security flags through the documented env flow
When an operator follows the repository's documented .env workflow, sets REQUIRE_LOGIN=true and REQUIRE_API_KEY=true, and then starts the public Cloudflare tunnel, those settings have no effect: docker-compose.example.yml passes both variables as literal false rather than expanding the values already provided by .env.example. The gateway consequently remains unauthenticated despite this checklist and becomes publicly reachable; make the compose use ${REQUIRE_LOGIN:-false} and ${REQUIRE_API_KEY:-false} (or explicitly document editing and recreating the service) before recommending exposure.
Useful? React with 👍 / 👎.
| ## Opção 1 — túnel Cloudflare (nativo do 9Router) | ||
|
|
||
| A tela **API Endpoint** tem o botão `Tunnel`. Ele registra um quick tunnel da | ||
| Cloudflare e devolve um endereço público `https://…trycloudflare.com` que | ||
| alcança o gateway sem abrir porta nenhuma no seu roteador. |
There was a problem hiding this comment.
Replace the Portuguese 9Router tunnel instructions
A Portuguese-speaking OmniRoute user following this option is told to use an API Endpoint screen and Tunnel button that this gateway does not have—the English version correctly states that these controls are 9Router-only and supplies the required cloudflared tunnel --url http://127.0.0.1:8082 command. Because the Portuguese section omits that manual command entirely, its Cloudflare option cannot be followed in this repository; translate the OmniRoute-specific flow instead.
Useful? React with 👍 / 👎.
| reais |= mapeamentos(c) | ||
| self.assertTrue(reais, "nenhum mapeamento de porta encontrado nos composes") |
There was a problem hiding this comment.
Compare documentation against the canonical compose
Because reais unions mappings from both the public example and the isolated test stack, documentation can diverge from docker-compose.example.yml while this test still passes. For example, changing the README gateway mapping to the test-only 127.0.0.1:19129:20128 or its panel to 127.0.0.1:19092:9090 is accepted even though readers would no longer see the production ports documented elsewhere. Compare public documentation with the canonical example compose rather than accepting a mapping from any test compose.
Useful? React with 👍 / 👎.
Portas
O
claudegravitydos artigos ocupa a 20128, porta padrão do 9Router. As stacks dos repositórios saem dessa faixa para que tudo rode junto na mesma máquina:2012880812012880824000808390909091/9092/9093Os exemplos de compose embutidos na documentação foram sincronizados — um exemplo que diverge do arquivo real é pior que exemplo nenhum: o leitor copia e fica com duas verdades.
Novo teste: toda porta citada na documentação tem de existir num compose do repositório. Verificado introduzindo
127.0.0.1:9999numa cópia descartável.Acesso remoto
Página nova na wiki — Remote Access — cobrindo túnel Cloudflare, Tailscale e proxy reverso, com a ordem que importa:
A ordem não é preferência. As stacks sobem com
REQUIRE_LOGIN=falseeREQUIRE_API_KEY=false, o que é seguro enquanto a porta está presa em127.0.0.1— e deixa de ser no instante em que o gateway ganha endereço público:/v1é prefixo público por projeto no 9Router (src/dashboardGuard.js), para que as ferramentas de código chamem o gateway sem sessão. Com chave de API desligada e URL pública, quem descobrir o endereço gasta as contas;requireLoginestá ligado. Com ele desligado, um gateway exposto entrega a própria configuração;A página separa duas coisas que o Tailscale faz e que se confundem: entrar (alcançar o gateway de fora — esta página) e sair (cada conta pelo seu endereço —
Egress and Multi-Session).Versões próprias para OmniRoute e LiteLLM, que não têm botão nativo: o caminho é
cloudflaredoutailscaleà mão, e no LiteLLM a proteção é a master key, não uma flag de login.https://claude.ai/code/session_01Dw2Zc66wvY8QBZJmPBeMsT