mudanza: decidir SIN teclas unitarias, /mnt/vvv entra al censo, y 25 rutas de secretos más
Tres cosas que salieron de usar la herramienta de verdad.
1. `--decidir <fichero>`: decidir EN LOTE desde `<ruta-o-nombre> <decision>` por línea.
El usuario: «no sé cómo activar o desactivar cosas aquí desde shuma llimphi remoto, que las
opciones son con teclas unitarias». El modo interactivo lee una tecla por entrada, y hay
terminales donde eso no se puede usar — una herramienta cuya única forma de decidir exige un
tipo de terminal no es una herramienta, es una herramienta PARA ESA TERMINAL. El fichero anda en
cualquiera, se revisa antes de aplicarlo, se versiona y se vuelve a correr.
Una clave que no empareja con nada, o que empareja con varias, es ERROR RUIDOSO y no se escribe
NADA: un lote a medias deja decidido lo que nadie revisó.
2. `/mnt/vvv` entra a las raíces del censo. No estaba, y ahí vive el trabajo: los repos de la
persona, el monorepo, el store, work/sources. El agujero se vio preguntando por `humanoid`: el
censo sólo conocía `/home/sergio/humanoid` —200 M de `build/` de junio— mientras el proyecto de
verdad, con su `.git`, estaba en `/mnt/vvv/humanoid`, fuera de toda raíz censada. `repos_git` SÍ
lo miraba: una parte del censo conocía el árbol y la otra no.
3. 25 rutas más en `rutas-fuera-de-git.txt`, del barrido que hizo el frente tawasuyu sobre su home:
`~/.wawa/seeds` (sin él ninguna app nueva del génesis nace), `~/.tejido` (la identidad de esta
máquina en la flota), `~/.local/share/agora`, `~/.config/{wawa,minga,thasnuna,shuma,mirada,hcloud}`,
`~/keys` y `~/fdroid` (⚠ firma de apps Android), `~/.gnupg`, `~/.pgpass`, `/etc/wireguard`,
`/etc/{shuma,sandokan,tawasuyu/agente,local.d}`. Son identidades y semillas: no se «vuelven a
generar», porque generar otras significa ser OTRA máquina para el resto de la flota.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1226,7 +1226,13 @@ def main():
|
||||
if i and http_ok:
|
||||
time.sleep(0.5) # no parecer un escaneo ante el propio fail2ban del objetivo
|
||||
doms.append(sondear_dominio(d, set(ident["ips"]), http_ok))
|
||||
raices = a.data_root or ["/var/www", "/var/lib", "/srv", "/opt", "/home", "/store"]
|
||||
# ⚠ `/mnt/vvv` ENTRA (2026-09-16). No estaba, y ahí vive el trabajo: los repos de la persona, el
|
||||
# monorepo, el store y `work/sources`. El censo listaba 169 cosas y ninguna era el árbol donde
|
||||
# está todo — el agujero apareció cuando el usuario preguntó por `humanoid` y el censo sólo
|
||||
# conocía `/home/sergio/humanoid`, que son 200 M de `build/` viejo: el proyecto de verdad, con
|
||||
# su `.git`, estaba en `/mnt/vvv/humanoid`, fuera de toda raíz censada.
|
||||
# (`repos_git` sí lo miraba: una parte del censo conocía el árbol y la otra no.)
|
||||
raices = a.data_root or ["/var/www", "/var/lib", "/srv", "/opt", "/home", "/store", "/mnt/vvv"]
|
||||
# Dos vistas y no una: el TOTAL por raíz es lo que dimensiona la mudanza (y lo que mira el
|
||||
# preflight), y el DETALLE por cosa es lo que se puede decidir. Dar sólo el total obliga a
|
||||
# decidir 25 G de una; dar sólo el detalle pierde el número que dice si entra en el destino.
|
||||
|
||||
@@ -601,6 +601,62 @@ def revisar(censo, interactivo=True):
|
||||
return censo
|
||||
|
||||
|
||||
def decidir_desde_fichero(censo, ruta_dec):
|
||||
"""Decidir EN LOTE desde un fichero de texto: `<ruta-o-nombre> <decision>` por línea.
|
||||
|
||||
── POR QUÉ EXISTE (pedido del usuario, 2026-09-16) ─────────────────────────────────────────
|
||||
«no sé cómo activar o desactivar cosas aquí desde shuma llimphi remoto, que las opciones son
|
||||
con teclas unitarias». El modo interactivo de `--decide` lee una tecla por entrada, y hay
|
||||
terminales —la consola remota de shuma, entre otras— donde eso no se puede usar. Una
|
||||
herramienta cuya única forma de decidir exige un tipo de terminal no es una herramienta: es una
|
||||
herramienta *para esa terminal*. El fichero funciona en cualquiera, se revisa antes de
|
||||
aplicarlo, se versiona y se vuelve a correr — que es lo mismo que ya se pide del plan.
|
||||
|
||||
Formato, deliberadamente pobre (`#` comenta, se ignoran líneas vacías):
|
||||
|
||||
/home/sergio/mapas muda
|
||||
terapeuta respalda # basta con que el nombre sea único
|
||||
/var/lib/swap muere
|
||||
|
||||
⚠ Una clave que no empareja con NADA es un ERROR RUIDOSO, no un aviso: significa que alguien
|
||||
decidió algo que no se aplicó, y el silencio ahí es exactamente la clase de fallo que este
|
||||
programa existe para no tener. Lo mismo si empareja con varias: se nombran todas y no se toca
|
||||
ninguna, porque elegir una por su cuenta sería adivinar.
|
||||
"""
|
||||
lineas = []
|
||||
with open(ruta_dec, encoding="utf-8") as fh:
|
||||
for i, l in enumerate(fh, 1):
|
||||
l = l.split("#", 1)[0].strip()
|
||||
if l:
|
||||
lineas.append((i, l))
|
||||
todos = entradas(censo)
|
||||
aplicadas, problemas = 0, []
|
||||
for i, l in lineas:
|
||||
partes = l.rsplit(None, 1)
|
||||
if len(partes) != 2 or partes[1] not in DECISIONES:
|
||||
problemas.append(f"línea {i}: «{l}» — falta la decisión o no es una de "
|
||||
f"{'/'.join(DECISIONES)}")
|
||||
continue
|
||||
clave, dec = partes
|
||||
cands = [(t, n, e) for t, n, e in todos
|
||||
if n == clave or os.path.basename(str(n)) == clave]
|
||||
if not cands:
|
||||
problemas.append(f"línea {i}: «{clave}» no empareja con NADA del censo")
|
||||
continue
|
||||
if len(cands) > 1:
|
||||
problemas.append(f"línea {i}: «{clave}» empareja con {len(cands)}: "
|
||||
+ ", ".join(n for _t, n, _e in cands[:5]))
|
||||
continue
|
||||
cands[0][2]["decision"] = dec
|
||||
aplicadas += 1
|
||||
if problemas:
|
||||
print("\n!! decisiones NO aplicadas:", file=sys.stderr)
|
||||
for x in problemas:
|
||||
print(f" {x}", file=sys.stderr)
|
||||
sys.exit("\n No se escribe nada: un lote a medias deja decidido lo que nadie revisó.")
|
||||
return aplicadas
|
||||
|
||||
|
||||
def guardar_censo(censo, ruta, respaldar=True):
|
||||
"""Vuelve a escribir el censo con las decisiones puestas, usando el MISMO emisor que lo creó.
|
||||
|
||||
@@ -1149,6 +1205,9 @@ def main():
|
||||
ap.add_argument("--revisar", action="store_true",
|
||||
help="sólo mostrar la revisión con recomendaciones, sin preguntar")
|
||||
ap.add_argument("--out", help="fichero del plan (default: stdout)")
|
||||
ap.add_argument("--decidir", metavar="FICHERO",
|
||||
help="decidir EN LOTE desde un fichero `<ruta-o-nombre> <decision>` por línea. "
|
||||
"Para terminales donde no se puede pulsar una tecla por entrada")
|
||||
ap.add_argument("--censo-out", help="dónde escribir el censo con las decisiones tomadas "
|
||||
"(default: sobre el mismo censo, con copia `.bak`)")
|
||||
ap.add_argument("--respaldo", help="corral donde va lo marcado `respalda` (host:ruta o ruta "
|
||||
@@ -1161,6 +1220,11 @@ def main():
|
||||
if a.revisar:
|
||||
revisar(censo, interactivo=False)
|
||||
return
|
||||
if a.decidir:
|
||||
n = decidir_desde_fichero(censo, a.decidir)
|
||||
destino_censo = a.censo_out or a.censo
|
||||
guardar_censo(censo, destino_censo, respaldar=(destino_censo == a.censo))
|
||||
print(f" {n} decisión(es) aplicadas → {destino_censo}")
|
||||
if a.decide:
|
||||
censo = revisar(censo, interactivo=True)
|
||||
# ── LAS DECISIONES SE GUARDAN, O NO EXISTIERON ──────────────────────────────────────────
|
||||
|
||||
@@ -29,3 +29,34 @@
|
||||
/var/lib/gitea # los repos servidos, la sqlite y las claves del gitea
|
||||
/etc/arje/cards.d # las tarjetas de arje: qué arranca esta máquina
|
||||
/ente # la Semilla de arje-zero
|
||||
|
||||
# ── AMPLIACIÓN 2026-09-16: el barrido del frente tawasuyu ───────────────────────────────────────
|
||||
# El agente de tawasuyu barrió SU home y encontró 572 KB de secretos que ninguna de las 15 rutas de
|
||||
# arriba miraba. Son identidades y semillas: no se «vuelven a generar», porque generar otras
|
||||
# significa ser OTRA máquina para el resto de la flota. Van acá para que el censo las vea sin que
|
||||
# nadie tenga que acordarse — que es el modo de fallo que esta lista existe para cerrar.
|
||||
~/.wawa/seeds # ⚠ el anillo de la ceremonia de capacidades del génesis: sin él NINGUNA app nueva de wawa nace
|
||||
~/.config/wawa # las tres llaves del operador de wawa (primaria, secundaria, recuperación)
|
||||
~/.tejido # la IDENTIDAD de esta máquina en tejido + el padrón de teléfonos dados de alta
|
||||
~/.local/share/agora # el grafo de confianza de ágora: el mismo padrón que usa `minga listen --admission`
|
||||
~/.config/minga # la frase del espejo (16 B): sin ella el post-commit se aparta EN SILENCIO
|
||||
~/.config/thasnuna # el token de telegram y el roster: INSTALAR.md dice que esto no va al repo a propósito
|
||||
~/.config/shuma # identidad x25519, token del gateway y pares conocidos de shuma
|
||||
~/.config/mirada # el escritorio afinado a mano: `perfiles-escritorio.ron` (178 KB) no lo genera nada
|
||||
~/.config/hcloud # credenciales de Hetzner Cloud
|
||||
~/.config/hammer # ⚠ ruta VIEJA del renombre, pero acá viven los `storagebox*.env` del respaldo
|
||||
~/.rumi/seeds # semillas de rumi
|
||||
~/keys # ⚠ claves de FIRMA DE APPS (`rizoma.p12`): perderlas impide ACTUALIZAR lo ya instalado
|
||||
~/fdroid # ⚠ `keystore.p12` firma el índice del repo F-Droid, y `config.yml` trae su contraseña
|
||||
~/.gnupg # el llavero GPG
|
||||
~/.pgpass # contraseñas de postgres
|
||||
~/.local/share/keyrings # llavero del escritorio
|
||||
~/.local/share/pki # certificados de cliente
|
||||
~/kkk # la credencial de deepseek (46 B): sin ella `scripts/agentes/deepseek` deja de existir
|
||||
~/.zshrc # el entorno de trabajo de la persona, afinado a mano
|
||||
~/.zshenv # ídem — y define rutas de las que dependen los guiones sueltos
|
||||
/etc/wireguard # ⚠ `server.key` y `wg0.conf`: la VPN de la flota. Otra clave = otros pares
|
||||
/etc/shuma # `gateway.env` del gateway de shuma
|
||||
/etc/sandokan # `cerebro.json`
|
||||
/etc/tawasuyu/agente # las autoridades del agente
|
||||
/etc/local.d # los `*.start` de OpenRC escritos a mano (tejido-relay, arje-boot-guard)
|
||||
|
||||
Reference in New Issue
Block a user