tawasuyu

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1572Shell 203Rust 109Markdown 993728 Sutichaykuna

pass: el gestor de contraseñas, y el hueco que el censo nombraba sin rodeos

La fila «contraseñas» del censo decía *«nada: la gente guarda sus claves en un
.txt o no usa la distro»*. Era de los pocos huecos SIN camino alternativo en
el corpus: no faltaba el nombre que alguien busca, faltaba la capacidad.

Y era el más barato de los tres: `pass` no compila nada —es un script de bash
sobre gpg— y su cadena entera ya estaba sellada (gnupg, libgcrypt, libassuan,
npth, pinentry, libksba, tree, util-linux, coreutils, git, wl-clipboard).

Lo que da valor a la receta son las deps de RUNTIME, no las de build: con sólo
`make` declarado sellaría perfecto y el paquete NO serviría. El script invoca
gpg ×31, git ×25, tree, getopt, base64, qrencode y wl-copy, y ninguna aparece
como dependencia de enlace porque no hay enlace que mirar — un script no tiene
NEEDED, así que el vigía de sonames no ayuda y la lista se escribe a mano
leyendo la fuente. Misma figura que `autoconf` con perl y m4.

Dos cosas que medí y cambiaron la receta:

· ME CORRIJO: no hace falta parchear nada para Wayland. Venía a cambiar xclip
  por wl-copy y 1.7.4 YA trae Wayland de primera opción (password-store.sh:158
  usa wl-copy/wl-paste; xclip es sólo la rama X11 de respaldo). Leer la fuente
  antes de parchear lo que uno cree.

· WITH_ALLCOMP=yes es reproducibilidad, no gusto: el Makefile decide QUÉ
  INSTALA preguntando si existen directorios del sistema de BUILD, sin DESTDIR
  (líneas 18-33). El lab no entra en hash_inputs ⇒ dos labs sellarían bytes
  distintos con el mismo hash. Forzándolo, la pregunta desaparece.

El guardián es funcional y pasó a la primera: crea una clave GPG de usar y
tirar, `pass init`, `pass insert`, `pass show` y COMPARA el secreto devuelto
con el guardado; además `pass ls` aparte, porque usa `tree` y su ausencia no
rompería `show` (pasaría desapercibida). Para un script, «existe y es
ejecutable» no prueba nada: el fichero se copia siempre.

6995dbb44eb733794d29bc8298cba3ac465ac783 · ruwaq Sergio · (1 p'unchaw ñawpaq) · tayta 66d4e5da6 · kay commitpi sach'ata qhaway

Semántico sayk'uy

Mana ima sutichaypas t'ikrakurqanchu: qillqalla, yachachiylla utaq willaylla t'ikrakurqan.

1 qillqakuna · +130 −0

diff --git a/recipes/pass.toml b/recipes/pass.toml
new file mode 100644
index 00000000..7d69eb9e
--- /dev/null
+++ b/recipes/pass.toml
@@ -0,0 +1,130 @@
+# pass 1.7.4 — el gestor de contraseñas de Unix. Cierra un hueco que el censo nombra sin rodeos.
+#
+# ══ POR QUÉ ENTRA ══════════════════════════════════════════════════════════════════════════════
+# `censo-distro.py` tenía la fila «contraseñas» en **2 de 3 ausente** con este contexto: *«nada: la
+# gente guarda sus claves en un .txt o no usa la distro»*. Es de los pocos huecos donde no hay
+# ningún camino alternativo en el corpus — no es que falte el nombre que alguien busca, es que no
+# hay con qué.
+#
+# Y es el más barato de los tres que faltaban: **`pass` no compila nada**, es un script de bash
+# sobre `gpg`, y toda su cadena ya estaba sellada (`gnupg`, `libgcrypt`, `libassuan`, `npth`,
+# `pinentry`, `libksba`, `tree`, `util-linux`, `coreutils`, `git`, `wl-clipboard`, `qrencode`).
+# Coste de construcción: cero. Lo que cuesta es declararlo bien, y de eso va el resto de esta nota.
+#
+# ══ ⚠ LO QUE DA VALOR A ESTA RECETA SON LAS DEPS DE RUNTIME, NO LAS DE BUILD ══════════════════
+# `pass` se «construye» con un `make install` que copia un script. Si sólo se declarara lo que hace
+# falta para eso —`make` y nada más— la receta **sellaría perfectamente y el paquete no serviría**:
+# el script invoca `gpg` 31 veces, `git` 25, `tree`, `getopt`, `base64`, `qrencode` y `wl-copy`, y
+# ninguna de esas aparece como dependencia de enlace porque NO HAY ENLACE QUE MIRAR.
+#
+# Es la misma figura que `autoconf` (que invoca `perl` y `m4` al ejecutarse) y la razón por la que
+# existe `runtime`: un script no tiene `NEEDED`, así que el vigía de sonames no lo puede ayudar y
+# **la única red es escribir la lista a mano leyendo la fuente**. Lo de arriba sale de contar las
+# invocaciones en `src/password-store.sh`, no de los `depends` de otra distro.
+#
+# ══ ME CORRIJO: NO HACE FALTA PARCHEAR NADA PARA WAYLAND ═══════════════════════════════════════
+# Venía a parchear `xclip` → `wl-copy`, porque esta distro es Wayland-only (X11 descartado para
+# todo el corpus) y `pass -c` sin portapapeles sería «sellado y sin la función que promete».
+# **Falso, y se ve abriendo el fichero**: 1.7.4 ya trae Wayland de PRIMERA opción
+# (`src/password-store.sh:158`):
+#
+#     clip() {
+#         if   [[ -n $WAYLAND_DISPLAY ]]; then copy_cmd=( wl-copy ); paste_cmd=( wl-paste -n )
+#         elif [[ -n $DISPLAY ]];         then copy_cmd=( xclip … )
+#         else die "Error: No X11 or Wayland display detected"
+#
+# ⇒ `wl-clipboard` es dep de runtime y `xclip` no hace falta; la rama X11 queda muerta por elección
+# de la distro, no por avería. Y sin ningún display `pass -c` **muere con un mensaje claro**, que es
+# el comportamiento correcto. Lección repetida: *leer la fuente antes de parchear lo que uno cree.*
+#
+# ══ ⚠ `WITH_ALLCOMP=yes` ES UNA CUESTIÓN DE REPRODUCIBILIDAD, NO DE GUSTO ══════════════════════
+# El Makefile decide **qué ficheros instala** preguntando si existen directorios DEL SISTEMA DE
+# BUILD, sin `DESTDIR` delante (líneas 18-33):
+#
+#     ifeq ($(WITH_BASHCOMP),)
+#     ifneq ($(strip $(wildcard $(BASHCOMPDIR))),)   # ⇐ /usr/share/bash-completion/completions
+#     WITH_BASHCOMP := yes
+#
+# O sea que el CONTENIDO del artefacto depende de cómo esté amueblado el lab. Y el lab **no entra en
+# `hash_inputs`**: dos labs distintos sellarían bytes distintos con la misma receta y el mismo hash,
+# que es la avería documentada en la memoria `lab-fuera-de-hash-inputs`. Forzando las tres a `yes` la
+# pregunta desaparece — se instalan siempre, y el artefacto no depende de quién lo construya.
+# (No se midió el lab a propósito: el punto es no DEPENDER de esa medición.)
+name    = "pass"
+version = "1.7.4"
+license = "GPL-2.0-or-later"
+
+[source]
+tarball = "https://git.zx2c4.com/password-store/snapshot/password-store-1.7.4.tar.xz"
+sha256  = "4c2d0a8b99df8915a87099607a8d912fd05d30651b6f014745c14e4ca8dbbfb7"
+
+[build]
+compiler = "zig-cc"
+target   = "x86_64-linux-musl"
+link     = "dynamic"
+flags    = []
+
+[build.phases]
+# Sin configure ni compilación: `make all` de upstream literalmente imprime «Password store is a
+# shell script, so there is nothing to do».
+configure = "true"
+compile   = "true"
+install = '''
+set -e
+make install DESTDIR=/out PREFIX=/usr WITH_ALLCOMP=yes
+
+[ -x /out/usr/bin/pass ] || { echo "pass: no quedó el ejecutable — NO sello" >&2; exit 1; }
+[ -f /out/usr/share/man/man1/pass.1 ] || { echo "pass: falta la página de manual" >&2; exit 1; }
+for c in /out/usr/share/bash-completion/completions/pass \
+         /out/usr/share/zsh/site-functions/_pass \
+         /out/usr/share/fish/vendor_completions.d/pass.fish; do
+  [ -f "$c" ] || { echo "pass: falta $c — WITH_ALLCOMP no surtió efecto y el artefacto quedaría a merced del lab" >&2; exit 1; }
+done
+
+# ⚠ GUARDIÁN FUNCIONAL: que GUARDE Y DEVUELVA un secreto de verdad.
+#
+# Para un script, «existe y es ejecutable» no prueba NADA: el fichero se copia siempre. Lo que puede
+# fallar es que no encuentre `gpg`, o que `getopt` sea el de BSD y no acepte opciones largas, o que
+# el `tree` que necesita no esté. Todo eso sella y revienta al primer uso.
+#
+# Así que acá se hace el ciclo completo: clave GPG de usar y tirar → `pass init` → `pass insert` →
+# `pass show`, y se compara el secreto devuelto con el guardado. Si no coincide, no se sella.
+export GNUPGHOME=/tmp/g-pass
+export PASSWORD_STORE_DIR=/tmp/pass-store
+rm -rf "$GNUPGHOME" "$PASSWORD_STORE_DIR"
+mkdir -p "$GNUPGHOME" "$PASSWORD_STORE_DIR"
+chmod 700 "$GNUPGHOME"
+export PATH="/out/usr/bin:$PATH"
+
+gpg --batch --pinentry-mode loopback --passphrase '' \
+    --quick-generate-key 'Guardian Takana <guardian@takana.invalid>' default default never \
+    >/tmp/gpg.log 2>&1 || { echo "pass: no se pudo crear la clave GPG de prueba — el guardián no puede concluir" >&2; tail -12 /tmp/gpg.log >&2; exit 1; }
+FPR=$(gpg --batch --with-colons --list-secret-keys 2>/dev/null | awk -F: '/^fpr:/{print $10; exit}')
+[ -n "$FPR" ] || { echo "pass: la clave se creó y no se puede leer su huella" >&2; exit 1; }
+
+pass init "$FPR" >/tmp/init.log 2>&1 || { echo "pass init FALLÓ" >&2; cat /tmp/init.log >&2; exit 1; }
+SECRETO="s3creto-de-guardian-$$"
+printf '%s\n' "$SECRETO" | pass insert --echo prueba/clave >/tmp/ins.log 2>&1 \
+  || { echo "pass insert FALLÓ — mirá si es getopt o gpg" >&2; cat /tmp/ins.log >&2; exit 1; }
+VUELTA=$(pass show prueba/clave 2>/tmp/show.log)
+[ "$VUELTA" = "$SECRETO" ] || { echo "pass: lo que devolvió NO es lo que se guardó ('$VUELTA' != '$SECRETO')" >&2; cat /tmp/show.log >&2; exit 1; }
+
+# `pass ls` usa `tree`: se comprueba aparte porque es la dep que más fácil se olvida y su ausencia
+# no rompe `show`, o sea que pasaría desapercibida.
+pass ls >/tmp/ls.log 2>&1 && grep -q 'prueba' /tmp/ls.log \
+  || { echo "pass ls no listó la entrada — probablemente falta 'tree'" >&2; cat /tmp/ls.log >&2; exit 1; }
+
+rm -rf "$GNUPGHOME" "$PASSWORD_STORE_DIR"
+echo "pass: ciclo completo OK — init + insert + show devolvió el mismo secreto, y ls lo listó"
+'''
+
+[deps]
+# Para construir: sólo `make` copia ficheros. Lo demás está acá porque el GUARDIÁN los ejecuta.
+build = ["make", "bash", "gnupg", "tree", "util-linux", "coreutils", "pinentry"]
+# ⚠ Ésta es la lista que hace que el paquete SIRVA, y sale de contar invocaciones en la fuente:
+# gpg ×31, git ×25, sed, dirname, mv, find, tail, qrencode ×4, grep, cat, tree ×3, mktemp, cp,
+# base64, wl-copy. `util-linux` por `getopt` (el script exige el ENHANCED, el de BSD no vale);
+# `bash` porque el shebang es bash y no sh; `pinentry` porque sin él gpg no puede pedir la frase de
+# paso en un uso interactivo real.
+runtime = ["bash", "gnupg", "pinentry", "tree", "util-linux", "coreutils", "git",
+           "wl-clipboard", "qrencode"]