Consolidación arquitectónica de sshd-como-servicio (Separación Mecanismo/
Política, decisión del usuario). El núcleo NO se toca: STAGE1_COMPONENTS +
STAGE1_SEED_CARD siguen siendo el mecanismo base atómico que el
selfhost-verify reconstruye bit a bit (of_tree 9adefb82/7fa6cb4e blindado).
Nuevo en hammer-bootstrap:
- SERVICE_COMPONENTS = [netup, openssh] (política de producto).
- SSHD_SERVICE_CARD: card genesis Native/Restart (netup + ssh-keygen -A +
exec sshd -D), validado E2E en QEMU.
- product_seed_card(): compone la seed de producto = seed base + cards de
servicio apendados al genesis vía serde_json (hammer sigue autocontenido,
sin dep de card-core). El núcleo (hammerd+getty) se preserva.
- assemble_product_rootfs() + product(): HIDRATACIÓN TARDÍA — hidrata el
stage1-rootfs ya sellado (4/4 verificado) + inyecta openssh/netup encima
+ escribe configs (passwd con sshd, sshd_config con PidFile /run, /var/empty
0711, /root/.ssh) y sella un `product-rootfs` APARTE. of_tree del núcleo
intacto. Idempotente, reproducible.
GOTCHA: los ficheros hidratados son hardlinks read-only al store ⇒ romper
el hardlink (remove+write) en vez de chmod (mutaría el inodo del store).
- CLI: `hammer bootstrap product --rootfs <base> --recipes recipes`.
- 4 tests nuevos (seed compone, hash determinista, assemble inyecta, recetas
de servicio parsean). 36/36 verde.
scripts/product-boot-test.sh: valida que el product-rootfs de la RUTA REAL
bootea en QEMU y sirve SSH (sólo provisiona authorized_keys de prueba, no
ensambla nada). VERDE: arje levanta hammerd+getty+sshd, handshake real
"Accepted publickey for root", guest responde (seed=hammer-product con 3
cards, Linux 6.16.12). Cierra el agujero de verificación con la arquitectura
final, no con el spike sucio.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>