commit 74505af9870bc3f30a9645e4c5ae41fd32cdefdb
parent f4fba944cb54b1a7072e6653cf300031af150f98
Author: Pablo Murad <pablo@pablomurad.com>
Date: Sat, 21 Mar 2026 16:31:41 -0300
fix(alt-protocols): redirect /~/user to /~/user/ and document Gemini paths (v0.10)
Molly Brown only maps HomeDocBase for paths ~/username/ with trailing slash.
Add TempRedirect for /~/user -> /~/user/. Clarify that gemini://host/user is not
the user capsule; README and skel copy updated.
Made-with: Cursor
Diffstat:
5 files changed, 22 insertions(+), 10 deletions(-)
diff --git a/README.md b/README.md
@@ -1 +1,7 @@
-runv.club setup
-\ No newline at end of file
+# runv-server
+
+Repositório de automação e documentação para **runv.club** (pubnix Debian).
+
+## Gemini (Molly Brown)
+
+O capsule de cada utilizador **não** está em `gemini://runv.club/USERNAME` (isso seria o path `/USERNAME`, que no servidor não corresponde à home). O formato correcto é **`gemini://runv.club/~/USERNAME/`** (path **`/~/USERNAME/`**), ou **`gemini://runv.club/~USERNAME/`** (redirect para o anterior). Requer Molly a correr, symlink em `/var/gemini/users/USERNAME`, home e `public_gemini` atravessáveis pelo utilizador do serviço — ver [`scripts/docs/alt_protocols.md`](scripts/docs/alt_protocols.md).
diff --git a/scripts/admin/create_runv_user.py b/scripts/admin/create_runv_user.py
@@ -418,7 +418,7 @@ Tudo o que colocares em **`public_html`** pode ser lido pelo mundo via HTTP no e
- **Gopher:** edita `~/public_gopher/gophermap` (e outros ficheiros nessa pasta). URL típica:
`gopher://{DEFAULT_GEMINI_HOST_PUBLIC}/1/~{username}` (o caminho exacto depende do servidor).
-- **Gemini:** edita `~/public_gemini/index.gmi`. URL canónica: `gemini://{DEFAULT_GEMINI_HOST_PUBLIC}/~/{username}/`; `gemini://{DEFAULT_GEMINI_HOST_PUBLIC}/~{username}/` também funciona (redirect no servidor).
+- **Gemini:** edita `~/public_gemini/index.gmi`. URL canónica: `gemini://{DEFAULT_GEMINI_HOST_PUBLIC}/~/{username}/` (path `/~/{username}/`, **com barra final**); `gemini://{DEFAULT_GEMINI_HOST_PUBLIC}/~{username}/` também funciona (redirect). `gemini://{DEFAULT_GEMINI_HOST_PUBLIC}/{username}` **não** é o teu capsule.
- Mantém **755** nas pastas públicas e **644** nos ficheiros, para o servidor conseguir ler.
## Comandos úteis na shell
@@ -478,7 +478,7 @@ iDocumentação: man gophermap (no pacote gophernicus). fake NULL 0
def default_gemini_index_gmi(username: str) -> str:
return f"""# ~{username} — runv.club (Gemini)
-Bem-vindo ao teu capsule em `gemini://{DEFAULT_GEMINI_HOST_PUBLIC}/~/{username}/` (canónica). `gemini://{DEFAULT_GEMINI_HOST_PUBLIC}/~{username}/` também funciona.
+Bem-vindo ao teu capsule em `gemini://{DEFAULT_GEMINI_HOST_PUBLIC}/~/{username}/` (canónica; barra final). `gemini://{DEFAULT_GEMINI_HOST_PUBLIC}/~{username}/` também funciona. `gemini://{DEFAULT_GEMINI_HOST_PUBLIC}/{username}` não aponta para aqui.
Edita este ficheiro em `~/public_gemini/index.gmi`. Mantém pastas **755** e ficheiros **644**.
diff --git a/scripts/admin/setup_alt_protocols.py b/scripts/admin/setup_alt_protocols.py
@@ -7,7 +7,7 @@ Infraestrutura Gopher (gophernicus) e Gemini (molly-brown) para runv.club.
Idempotente, dry-run, subprocess sem shell. Executar como root no Debian.
-Versão 0.09 — runv.club
+Versão 0.10 — runv.club
"""
from __future__ import annotations
@@ -32,7 +32,7 @@ from typing import Any, Final
# Constantes
# ---------------------------------------------------------------------------
-VERSION: Final[str] = "0.09"
+VERSION: Final[str] = "0.10"
LETSENCRYPT_LIVE: Final[Path] = Path("/etc/letsencrypt/live")
LETSENCRYPT_ARCHIVE: Final[Path] = Path("/etc/letsencrypt/archive")
@@ -73,7 +73,7 @@ iEdita este ficheiro em ~/public_gopher/gophermap. fake NULL 0
DEFAULT_USER_INDEX_GMI: Final[str] = """# ~{username} — runv.club (Gemini)
-Bem-vindo ao teu capsule em `gemini://runv.club/~/{username}/` (URL canónica Molly). O endereço `gemini://runv.club/~{username}/` também funciona (redirect no servidor).
+Bem-vindo ao teu capsule em `gemini://runv.club/~/{username}/` (URL canónica Molly: path `/~/{username}/` com **barra final**). O endereço `gemini://runv.club/~{username}/` também funciona (redirect). **Não** confundas com `gemini://runv.club/{username}` — esse path **não** é o capsule (no Molly só há homes em `/~/…`).
Edita este ficheiro em `~/public_gemini/index.gmi`. Mantém pastas **755** e ficheiros **644** para o servidor ler o conteúdo.
@@ -294,8 +294,11 @@ GeminiExt = "gmi"
ReadMollyFiles = true
# Molly Brown resolve espaços de utilizador em paths /~/user/… (HomeDocBase).
+# A documentação oficial exige o prefixo ~/username/ *com* barra final; sem ela,
+# /~/user não casa e devolve 51 — redireccionamos para /~/user/.
# URLs estilo Apache /~user/… redireccionam sem tocar em ficheiros no disco.
[TempRedirects]
+"^/~/([^/]+)$" = "/~/$1/"
"^/~([^/]+)(/.+)$" = "/~/$1$2"
"^/~([^/]+)/?$" = "/~/$1/"
"""
diff --git a/scripts/docs/alt_protocols.md b/scripts/docs/alt_protocols.md
@@ -17,12 +17,16 @@ Script em **`scripts/admin/setup_alt_protocols.py`**: instala e configura **goph
- **Gopher (gophernicus):** selectors **`~username/…`** (tilde **colado** ao nome), alinhado com URLs como **`gopher://runv.club/1/~user`**. Não há o mesmo «split» de path que no Molly.
- **Gemini (Molly Brown):** o servidor resolve caps em **`/~/username/…`**. URLs estilo Apache **`/~username/…`** são aceites graças a **`[TempRedirects]`** no `.conf` gerado pelo script (**v0.09+**). Pode usar indistintamente **`gemini://runv.club/~/user/`** (canónico) ou **`gemini://runv.club/~user/`** (compatível).
+### URLs Gemini que *não* são capsules de utilizador
+
+O Molly **não** espelha o HTTP `mod_userdir` no mesmo path: **`gemini://runv.club/pmurad`** (path **`/pmurad`**) **não** aponta para a home — não existe ficheiro em `/var/gemini/pmurad`. O capsule está em **`gemini://runv.club/~/pmurad/`** (path **`/~/pmurad/`**, com **`~/`** e **barra final**). Sem a barra final, **`gemini://runv.club/~/pmurad`** era comum falhar com **51 Not found**; a partir do **v0.10** o `.conf` inclui redirect **`/~/user` → `/~/user/`**.
+
## Travessia da home (`755` na política runv)
Apache (`mod_userdir`), **gophernicus** e **molly-brown** precisam de **execução para «others»** (`o+x`, mínimo) em **cada** componente do caminho até a pasta pública (`~/public_html`, `~/public_gopher`, `~/public_gemini`). O utilizador de runtime **não é o mesmo** em todos: no Debian o Molly costuma correr como **`www-data`**; o **gophernicus** usa o **`User=`** do unit (tipicamente `gophernicus`) — veja `/lib/systemd/system/gophernicus@.service`. Uma home em **`700`** impede a travessia: **HTTP, Gopher e Gemini** deixam de servir conteúdo (p.ex. Gemini **«Not found»** com `index.gmi` presente).
- **Novas contas:** [`create_runv_user.py`](../admin/create_runv_user.py) aplica **`755`** na home em `apply_runv_permissions`.
-- **Backfill:** a partir do **v0.07**, [`setup_alt_protocols.py`](../admin/setup_alt_protocols.py) repõe a home do utilizador para **`755`** quando o modo actual é outro (com registo em log). O **v0.08** corrige a detecção de caminhos Let's Encrypt quando `live`/`archive` são **symlinks** (o bloco LE deixa de saltar incorrectamente). O **v0.09** adiciona redirects Molly `~user` → `~/user` e validação **`test -r`** do `gophermap` com o utilizador do serviço gophernicus.
+- **Backfill:** a partir do **v0.07**, [`setup_alt_protocols.py`](../admin/setup_alt_protocols.py) repõe a home do utilizador para **`755`** quando o modo actual é outro (com registo em log). O **v0.08** corrige a detecção de caminhos Let's Encrypt quando `live`/`archive` são **symlinks** (o bloco LE deixa de saltar incorrectamente). O **v0.09** adiciona redirects Molly `~user` → `~/user` e validação **`test -r`** do `gophermap` com o utilizador do serviço gophernicus. O **v0.10** adiciona redirect **`/~/user` → `/~/user/`** (barra final exigida pelo Molly para `HomeDocBase`).
- **Conflito:** [`patches/patch_permissions.py`](../../patches/patch_permissions.py) pode aplicar **`chmod 700`** em cada `/home/<user>` por política de privacidade — isso **quebra** a hospedagem em `public_*` até voltar a alinhar permissões (provisionamento ou `chmod` manual).
## Let's Encrypt e chave TLS (v0.07+; symlinks v0.08+)
@@ -129,7 +133,7 @@ sudo python3 scripts/admin/setup_alt_protocols.py --verbose
|------|--------|
| `--dry-run` | Simula; não grava (validação de root ignorada em alguns passos só se documentado). |
| `--verbose` | Log detalhado. |
-| `--force` | Sobrescreve configs de sistema (com backup com timestamp) e ficheiros modelo no backfill (exceto **`~/public_gemini/index.gmi`** se já existir). Necessário para **regravar** `/etc/molly-brown/runv.club.conf` (incl. **`[TempRedirects]`** v0.09+) e remover o drop-in obsoleto **`50-runv-logs.conf`** (v0.05) ao migrar logs para `/var/lib/molly-brown/`. |
+| `--force` | Sobrescreve configs de sistema (com backup com timestamp) e ficheiros modelo no backfill (exceto **`~/public_gemini/index.gmi`** se já existir). Necessário para **regravar** `/etc/molly-brown/runv.club.conf` (incl. **`[TempRedirects]`** v0.09+ e redirect **`/~/user/`** v0.10) e remover o drop-in obsoleto **`50-runv-logs.conf`** (v0.05) ao migrar logs para `/var/lib/molly-brown/`. |
| `--skip-install` | Não corre `apt-get`. |
| `--skip-gopher` / `--skip-gemini` | Ignora pacote, config e serviço desse protocolo. |
| `--skip-firewall` | Não altera UFW. |
diff --git a/tools/skel/public_gemini/index.gmi b/tools/skel/public_gemini/index.gmi
@@ -1,6 +1,6 @@
# Capsule Gemini — runv.club
-O endereço canónico é `gemini://runv.club/~/NOME_UTILIZADOR/` (substitui pelo teu username Unix). `gemini://runv.club/~NOME_UTILIZADOR/` também funciona (redirect no servidor). Gopher usa selector `~NOME` sem barra extra — ver documentação do projeto.
+O endereço canónico é `gemini://runv.club/~/NOME_UTILIZADOR/` (substitui pelo teu username Unix; **barra final**). `gemini://runv.club/~NOME_UTILIZADOR/` também funciona (redirect). `gemini://runv.club/NOME_UTILIZADOR` (sem `~/`) **não** é este capsule. Gopher usa selector `~NOME` sem barra extra — ver documentação do projeto.
Edita este ficheiro em `~/public_gemini/index.gmi`. Ficheiros `.gmi` são Texto Gemini; mantém pastas 755 e ficheiros 644 para o servidor conseguir ler.