#!/usr/bin/env python3 """declarar.py — convierte los servicios de un censo en una SEMILLA de arje (SDD 29 etapa 3). El paso que cierra la mudanza. Copiar un binario no lo levanta al arrancar: hay que DECLARARLO. Esto toma lo que el censo leyó de `/proc` —la invocación real— y emite `seed.card.json`. ── POR QUÉ NO ALCANZA CON `arje-absorb` ──────────────────────────────────────────────────────── `arje-absorb` ya traduce sysvinit/runit/dinit/openrc a una Semilla, y está bien hecho. Pero lee **la declaración del init ajeno**, que es exactamente la que miente: en gioser `rc-status` daba `stopped` para cinco servicios que estaban vivos, y los que NADIE declara —los `no-declarado`, que son el trabajo real— no aparecen en ninguna. Absorber la declaración reproduce el agujero. Esto lee lo VIVO. Los dos se complementan: absorb para lo declarado, esto para lo que corre. ── LAS TRES REGLAS ───────────────────────────────────────────────────────────────────────────── 1. **Un servicio sin `cmdline` legible NO se emite**, y se dice. Una tarjeta que no puede arrancar es peor que una tarjeta ausente: la ausente falla ruidosamente al primer arranque, la rota arranca el sistema y deja el servicio caído sin que nada avise. 2. **El `cwd` se preserva envolviendo**, porque el payload `Native` de arje acepta `exec`/`argv`/ `envp` y **NO `cwd`** (comprobado sobre la semilla real). Un servicio que depende de su directorio —como `puerta-f6e393ff`, que corre con `cwd=/mnt/vvv/tawasuyu/shared/tejido`— arrancaría en `/` y fallaría de un modo difícil de atribuir. Se envuelve en `sh -c 'cd … && exec …'`, que es el idioma que la propia tarjeta de `sshd` ya usa. 3. **Los IDs son DETERMINISTAS**, derivados del nombre. El mismo censo produce la misma semilla byte a byte, así que dos corridas se pueden comparar con `diff` — que es lo que vuelve revisable un fichero generado. ⚠ NO se copia el entorno. El censo no lo lee (trae tokens), así que un servicio que dependa de variables de entorno va a necesitar que alguien las escriba. Se avisa por nombre. Uso: scripts/mudanza/declarar.py --censo censo.toml [--label mi-servidor] --out seed.card.json """ import argparse, hashlib, json, os, shlex, sys, tomllib sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from planear import origen_binario, alternativas, _REPO # noqa: E402 from ids import ulid_determinista # noqa: E402 def tarjeta(nombre, cmdline, cwd, puertos): """Una entrada `genesis`. El molde sale de la semilla real del producto.""" argv_all = shlex.split(cmdline) if not argv_all: return None if cwd and cwd not in ("/", ""): # Regla 2: `Native` no tiene `cwd`. Se envuelve, igual que la tarjeta de sshd del producto. exec_, argv = "/bin/sh", ["-c", f"cd {shlex.quote(cwd)} && exec {cmdline}"] else: exec_, argv = argv_all[0], argv_all[1:] return { "flow": {"input": [], "output": []}, "genesis": [], "id": ulid_determinista("takana-mudanza", nombre), "label": nombre, "lifecycle": "daemon", "lineage": None, "payload": {"Native": {"argv": argv, "envp": [], "exec": exec_}}, "permissions": {"filesystem": "read-write", "ipc": {"allow": []}, "networking": "full", "processes": True}, "priority": "normal", "provides": [], "requires": [], "schema_version": 1, "soma": { "cgroup": {"cpu_weight": None, "io_weight": None, "path": f"arje.slice/{nombre}"}, "cpu_affinity": None, "namespaces": {k: False for k in ("cgroup", "ipc", "mount", "net", "pid", "user", "uts")}, "rlimits": {"mem_bytes": None, "nofile": None, "nproc": None}, }, "supervision": {"Restart": {"initial": 500, "max": 20000}}, "_mudanza": {"puertos": puertos, "cwd_original": cwd}, } def perfil(censo, label, repo): """Emite un `[perfil.