Skip to content

feat: acesso remoto documentado e cada gateway na sua porta no host - #7

Merged
elielsousa-pathbit merged 1 commit into
masterfrom
feat/acesso-remoto-e-portas
Sep 12, 2026
Merged

elielsousa-pathbit merged 1 commit into
masterfrom
feat/acesso-remoto-e-portas

Conversation

@elielsousa-pathbit

Copy link
Copy Markdown
Contributor

Portas

O claudegravity dos 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:

Serviço Interna Host
9Router 20128 8081
OmniRoute 20128 8082
LiteLLM 4000 8083
Painéis 9090 9091 / 9092 / 9093

Os 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:9999 numa 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:

1. exigir login e definir senha própria
2. exigir chave de API
3. só então expor

A ordem não é preferência. As stacks sobem com REQUIRE_LOGIN=false e REQUIRE_API_KEY=false, o que é seguro enquanto a porta está presa em 127.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;
  • as rotas administrativas do gateway só são protegidas quando requireLogin está ligado. Com ele desligado, um gateway exposto entrega a própria configuração;
  • o bloqueio do botão de túnel enquanto o login está desligado vive na tela: o endpoint não reavalia a condiçã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 é cloudflared ou tailscale à mão, e no LiteLLM a proteção é a master key, não uma flag de login.

https://claude.ai/code/session_01Dw2Zc66wvY8QBZJmPBeMsT

**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.
@elielsousa-pathbit
elielsousa-pathbit merged commit a984511 into master Sep 12, 2026
5 checks passed
@elielsousa-pathbit
elielsousa-pathbit deleted the feat/acesso-remoto-e-portas branch September 12, 2026 21:42

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1865598959

ℹ️ 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".

# Porta interna 20128 (padrao do 9Router); publicada em 8081 no host.
# A 20128 do host fica para a stack do artigo (claudegravity), que usa
# a porta padrao -- assim as duas rodam juntas sem colidir.
- "127.0.0.1:8081:20128"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update host-facing URLs to use the new port

When this compose is used from the host, the gateway now listens on 8081, but NEXT_PUBLIC_BASE_URL immediately below and the repository's local ROUTER_URL defaults still point to 127.0.0.1:20128. Consequently, client-facing URLs generated by 9Router and locally run 9RTKSync diagnostics can target the article stack or an unused port instead of this gateway; migrate the host-facing defaults to 8081 while retaining http://9router:20128 for container-to-container traffic.

Useful? React with 👍 / 👎.

Comment on lines +99 to +103
# 3. Publish the gateway on the tailnet interface instead of loopback
# (in the compose, replace 127.0.0.1 with the tailnet address)
ports:
- "100.101.102.103:20128:20128"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the assigned host port in the Tailscale recipe

When multiple documented gateways run on the same host, this recipe assigns them all the shown Tailnet port 20128, despite the commit allocating 8081, 8082, and 8083 specifically so they coexist. Following the stated “same recipe” for 9Router, OmniRoute, and LiteLLM therefore creates port conflicts on the Tailnet address; only the bind address should change here, while each gateway keeps its allocated host port (for this stack, 100.101.102.103:8081:20128) and matching client URL.

Useful? React with 👍 / 👎.

Comment on lines +147 to +149
# The synchronizer panel should not be exposed at all
curl -si https://<your-address>:9091/ | head -1 # expect connection refused
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Check the panel over its actual HTTP protocol

If the panel is accidentally reachable on port 9091, this HTTPS request still fails because the embedded dashboard serves plain HTTP, so the check cannot distinguish an exposed panel from a closed port; piping into head also hides curl's failure status. A user can therefore receive the expected-looking failure while the credential dashboard remains publicly reachable over http://<address>:9091; probe the actual HTTP endpoint or perform an explicit TCP-port check.

Useful? React with 👍 / 👎.

import unittest

RAIZ = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
RX_PORTA = re.compile(r'-\s*"127\.0\.0\.1:(\d+):(\d+)"')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate non-loopback documentation mappings too

The new consistency test hard-codes 127.0.0.1, so it silently ignores every remote-access mapping, including the newly added 100.101.102.103:20128:20128 example that already disagrees with the compose's 8081:20128 host mapping. Thus the test does not enforce its stated invariant precisely where this change introduces alternate bind addresses; parse arbitrary bind addresses and compare the published/internal port pair.

Useful? React with 👍 / 👎.

# Porta interna 20128 (padrao do 9Router); publicada em 8081 no host.
# A 20128 do host fica para a stack do artigo (claudegravity), que usa
# a porta padrao -- assim as duas rodam juntas sem colidir.
- "127.0.0.1:8081:20128"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update host-facing URLs to use the new port

When this compose is used from the host, the gateway now listens on 8081, but NEXT_PUBLIC_BASE_URL immediately below and the repository's local ROUTER_URL defaults still point to 127.0.0.1:20128. Consequently, client-facing URLs generated by 9Router and locally run 9RTKSync diagnostics can target the article stack or an unused port instead of this gateway; migrate the host-facing defaults/docs with the port change (while retaining the service URL inside Compose).

Useful? React with 👍 / 👎.

Comment on lines +147 to +148
# The synchronizer panel should not be exposed at all
curl -si https://<your-address>:9091/ | head -1 # expect connection refused

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Check the panel over its actual HTTP protocol

If the panel is accidentally exposed on port 9091, this HTTPS probe still fails because the synchronizer serves plain HTTP there, so it cannot distinguish an exposed panel from a refused connection; piping through head also hides curl's failing exit status. In the exact misconfiguration this safety check is meant to detect, a reader can therefore conclude the panel is private while http://<address>:9091/ remains reachable. Probe the panel with HTTP or perform an explicit TCP check.

Useful? React with 👍 / 👎.

import unittest

RAIZ = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
RX_PORTA = re.compile(r'-\s*"127\.0\.0\.1:(\d+):(\d+)"')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate non-loopback mappings too

The new consistency test hard-codes 127.0.0.1, so it ignores every mapping that uses a tailnet or other interface address—including the newly added 100.101.102.103:20128:20128 examples. That allows the inconsistent host ports in this same commit, and future remote-access examples, to pass despite the test's stated guarantee that every documented mapping matches a compose. Parse the address independently and compare the host/internal port pair for non-loopback mappings as well.

Useful? React with 👍 / 👎.

Comment on lines +151 to +153
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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Provide a route before promising remote panel access

With the panel mapping kept on 127.0.0.1, a remote tailnet client cannot connect to <tailnet-ip>:9091, and 9Router's built-in Cloudflare tunnel forwards to the gateway rather than this separate synchronizer service. Thus neither setup documented above makes the panel reachable as this sentence promises. Document a separate SSH/local port forward, Tailscale Serve or tunnel route, or bind the panel specifically to the tailnet address.

Useful? React with 👍 / 👎.

Comment on lines +89 to +90
**Setting it up by hand**, which is also how you do it for OmniRoute and LiteLLM:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Document LiteLLM authentication before exposing it

The page explicitly directs OmniRoute and LiteLLM users to this remote-access recipe, but the mandatory authentication steps above only name 9Router's REQUIRE_LOGIN and REQUIRE_API_KEY flags. LiteLLM does not use those flags and must instead be protected with its master key; because the page never mentions or configures that prerequisite, a LiteLLM user following the advertised recipe can publish an unauthenticated proxy backed by paid accounts. Add gateway-specific authentication steps before the shared Tailscale instructions.

Useful? React with 👍 / 👎.

Comment thread tools/valida_docs.py
Comment on lines +163 to 165
contexto = "".join(linhas[max(0, n - 3):n + 1])
if RX_ROTA_DO_GATEWAY.search(contexto):
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restrict gateway context to the actual route statement

This rolling four-line context suppresses validation for unrelated project routes whenever a nearby line mentions the gateway. In the current README, the gateway diagnostic at line 261 causes /api/change-password, /api/sync, and /api/cron-run on the following lines to be skipped, so deleting any of those handlers would no longer fail the documentation test. Determine ownership from the route's actual sentence or a real paragraph/block rather than an arbitrary neighboring-line window.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant