From 8592bab19ee819aef527a3654676676c97aa679d Mon Sep 17 00:00:00 2001 From: sergio Date: Mon, 18 May 2026 18:40:05 +0000 Subject: [PATCH] =?UTF-8?q?docs(arje):=20organiza=20core/=20+=20seeds=20ca?= =?UTF-8?q?n=C3=B3nicas=20+=20boot=20reproducible?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - crates/core/README.md: agrupamiento lógico de los 31 crates absorbidos de arje (ente-*) y del protocolo brahman (brahman-*) en 6 grupos — Init/PID 1, contratos, discovery, IPC+CAS, cerebro, 14 shims compat systemd. No se movieron crates físicamente (rompería paths cross-workspace). - seeds/arje-minimal.card.json: PID1 + /bin/sh, smoke test QEMU. - seeds/arje-prod.card.json: PID1 + 14 shims compat + tmpfiles/binfmt one-shots + echo + getty (16 children). Validados con brahman_card::Card::validate. - seeds/validate.sh: carga la seed vía ente-zero en dev mode. - scripts/build-arje-initrd.sh: empaqueta CPIO+gzip newc layout /init→/sbin/ente-zero, /usr/sbin/ente-*-compat, /ente/seed.card.json, /bin/{sh,...} (busybox o glibc+ldd). Tested: produce 20 MB initrd OK. - scripts/run-arje-qemu.sh: qemu-system-x86_64 con KVM auto-detect, -kernel/-initrd/-append "rdinit=/init console=ttyS0,115200 panic=10". - docs/arje-boot.md: doc end-to-end — layout initramfs, QEMU (con kernel del host o externo), GRUB bare metal, Proxmox/libvirt args:, schema de Card con todas las validaciones, debugging (sockets de introspección, snapshot/restore, metrics), checklist pre-deploy. Co-Authored-By: Claude Opus 4.7 --- .gitignore | 1 + crates/core/README.md | 100 +++++++++++ docs/arje-boot.md | 317 +++++++++++++++++++++++++++++++++++ scripts/build-arje-initrd.sh | 140 ++++++++++++++++ scripts/run-arje-qemu.sh | 90 ++++++++++ seeds/arje-minimal.card.json | 62 +++++++ seeds/arje-prod.card.json | 296 ++++++++++++++++++++++++++++++++ seeds/validate.sh | 40 +++++ 8 files changed, 1046 insertions(+) create mode 100644 crates/core/README.md create mode 100644 docs/arje-boot.md create mode 100755 scripts/build-arje-initrd.sh create mode 100755 scripts/run-arje-qemu.sh create mode 100644 seeds/arje-minimal.card.json create mode 100644 seeds/arje-prod.card.json create mode 100755 seeds/validate.sh diff --git a/.gitignore b/.gitignore index b86de51..42b39a3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ Cargo.lock.bak .DS_Store .claude/ +/out/ diff --git a/crates/core/README.md b/crates/core/README.md new file mode 100644 index 0000000..652fcad --- /dev/null +++ b/crates/core/README.md @@ -0,0 +1,100 @@ +# `crates/core/` — Init Arje (absorbido) + Protocolo Brahman + +El directorio agrupa **dos linajes** que se fusionaron al absorberse arje +dentro del workspace de brahman: + +| linaje | prefijo | función | +| ----------- | ------------ | ------------------------------------------------ | +| `arje` | `ente-*` | Init (PID 1), encarnación Linux, compat systemd | +| `brahman` | `brahman-*` | Tarjeta canónica, handshake, broker, admin | + +No están en sub-carpetas físicas porque el workspace declara los paths uno +a uno en `Cargo.toml` raíz y muchos `Cargo.toml` hijos usan `path = +"../ente-X"`. El agrupamiento siguiente es **lógico**: cada crate se +encuentra como `crates/core/`. + +--- + +## 1. Init / PID 1 + +| crate | tipo | resumen | +| --------------- | ---------- | ------------------------------------------------------------------ | +| `ente-zero` | binario | PID 1 del fractal. Bucle primordial (reap + bus + handshake). | +| `ente-kernel` | lib | `bootstrap_kernel_surface()`, subreaper, SIGCHLD/uevent streams. | +| `ente-soma` | lib (shim) | Re-export sobre `crates/shared/ente-incarnate` (clone+ns+cgroup). | +| `ente-snapshot` | lib | `FractalSnapshot` JSON — checkpoint/restore del grafo de Cards. | + +## 2. Contratos canónicos + +| crate | resumen | +| ------------------ | -------------------------------------------------------------------- | +| `brahman-card` | `Card { soma, payload, flow, permissions, supervision, genesis }`. | +| `brahman-card-wit` | Extracción de interfaces WIT de componentes WASM. | +| `brahman-cards` | Helpers para construir Cards típicas (consumer/producer/broker). | +| `ente-card` | Alias histórico — re-export de `brahman-card` con nombres legacy. | + +## 3. Discovery / Routing + +| crate | resumen | +| -------------------- | ------------------------------------------------------------------ | +| `brahman-handshake` | Protocolo Init↔módulo (Hello, Ping, ListSessions) postcard/Unix. | +| `brahman-broker` | Service locator: empareja `flow.input` ↔ `flow.output` por tipo. | +| `brahman-admin` | Socket separado para snapshots de sesiones + matches. | + +## 4. IPC interno + Storage + +| crate | resumen | +| ------------- | ----------------------------------------------------------------------- | +| `ente-bus` | Unix SOCK_STREAM con framing postcard. `Announce`/`Invoke`/`ListEntes`. | +| `ente-cas` | Content-addressed storage SHA-256 (blobs Wasm, audit log). | +| `ente-wasm` | Encarna `Payload::Wasm` vía `wasmi` en thread dedicado. | + +## 5. Cerebro / Observabilidad + +| crate | resumen | +| ------------- | ------------------------------------------------------------------------ | +| `ente-brain` | Rule engine + observer estadístico + audit log con hash chain a CAS. | +| `ente-echo` | Ente de prueba — provee `Capability::Endpoint(echo)` para smoke tests. | + +## 6. Compat systemd (shims D-Bus) + +Cada shim es un binario que se anuncia con un nombre well-known +`org.freedesktop.X1` y traduce las llamadas al bus interno. Esto permite +que GNOME/KDE/aplicaciones legacy arranquen sobre arje sin systemd: + +| binario | reemplaza | nombre D-Bus | +| --------------------------- | --------------------- | ----------------------------------- | +| `ente-logind-compat` | `systemd-logind` | `org.freedesktop.login1` | +| `ente-hostnamed-compat` | `systemd-hostnamed` | `org.freedesktop.hostname1` | +| `ente-timedated-compat` | `systemd-timedated` | `org.freedesktop.timedate1` | +| `ente-localed-compat` | `systemd-localed` | `org.freedesktop.locale1` | +| `ente-journald-compat` | `systemd-journald` | `org.freedesktop.LogControl1` | +| `ente-resolved-compat` | `systemd-resolved` | `org.freedesktop.resolve1` | +| `ente-polkit-compat` | `polkitd` | `org.freedesktop.PolicyKit1` | +| `ente-machined-compat` | `systemd-machined` | `org.freedesktop.machine1` | +| `ente-systemd1-compat` | `systemd` (Manager) | `org.freedesktop.systemd1` | +| `ente-notify-compat` | `sd_notify` socket | `/run/systemd/notify` (datagram) | +| `ente-timer-compat` | `systemd-timer` | (cron-like, sin D-Bus) | +| `ente-tmpfiles-compat` | `systemd-tmpfiles` | (aplica tmpfiles.d al boot) | +| `ente-binfmt-compat` | `systemd-binfmt` | (registra binfmt_misc handlers) | +| `ente-policy-provider` | (interno) | proveedor de decisiones polkit | + +--- + +## Crates relacionados fuera de `core/` + +Dependen del Init pero viven en `crates/shared/`: + +- `ente-incarnate` — rutina pura de `clone(2) + namespaces + cgroup + + rlimits + cpu_affinity`. Reusable por shipote y supervisores no-PID-1. +- `brahman-net` — malla P2P opcional (libp2p) que extiende el handshake. +- `brahman-sidecar` — helper `spawn(card)` para que las apps se presenten + al Init sin reimplementar el cliente del handshake. + +## Convención de uso + +Para arrancar el Init y ejecutar Cards, ver: + +- **Seeds estándar** en `seeds/`. +- **Build de initramfs** con `scripts/build-arje-initrd.sh`. +- **Boot en QEMU / bare metal** documentado en `docs/arje-boot.md`. diff --git a/docs/arje-boot.md b/docs/arje-boot.md new file mode 100644 index 0000000..023a3a3 --- /dev/null +++ b/docs/arje-boot.md @@ -0,0 +1,317 @@ +# Booteando arje — initramfs, QEMU y bare metal + +`arje` es el init absorbido por brahman: `ente-zero` corre como PID 1, lee +la **Tarjeta Semilla** (`/ente/seed.card.json`), monta `/proc /sys /dev +/sys/fs/cgroup`, y encarna recursivamente cada Card declarada en +`genesis` vía `ente-incarnate::Incarnator` (`clone(2)` + namespaces + cgroup v2). + +Este documento describe el ciclo completo: + +1. **Layout** del initramfs. +2. **Build** del initrd (`scripts/build-arje-initrd.sh`). +3. **Boot en QEMU** (`scripts/run-arje-qemu.sh`). +4. **Boot en bare metal / VM persistente** (entrada de GRUB). +5. **Tarjeta Semilla** — qué pone, qué valida, cómo customizar. +6. **Debugging**: tracing, sockets de introspección, snapshot/restore. + +--- + +## 1. Layout del initramfs + +El initrd es un **CPIO + gzip** (formato `newc`, estándar Linux). Su raíz: + +``` +/init wrapper sh, ejecuta /sbin/ente-zero +/sbin/ente-zero PID 1 +/usr/sbin/ente-echo +/usr/sbin/ente-policy-provider +/usr/sbin/ente-*-compat 13 shims D-Bus (logind, hostnamed, …) +/ente/seed.card.json Tarjeta Semilla canónica +/bin/{sh,ls,mount,…} busybox-static (o glibc dinámica + libs) +/etc, /dev, /proc, /sys, /run mountpoints vacíos +/sys/fs/cgroup mountpoint cgroup v2 +``` + +Path canónico de la semilla en **prod**: `/ente/seed.card.json` (ver +`crates/core/ente-zero/src/seed.rs`). En **dev** (no PID 1): +`./seed.card.json` en el cwd, o se sintetiza una mínima. + +## 2. Build del initrd + +Requisitos host: `cargo`, `cpio`, `gzip`, y opcionalmente `busybox-static` +(en Debian/Ubuntu: `apt install busybox-static`). + +```bash +# Default: semilla de prod, sale en out/arje.initrd.cpio.gz +scripts/build-arje-initrd.sh + +# Customizar: +scripts/build-arje-initrd.sh seeds/arje-minimal.card.json out/min.cpio.gz + +# Sin busybox del sistema, apuntar a uno propio: +BUSYBOX_BIN=/path/to/busybox scripts/build-arje-initrd.sh + +# Vendorear binarios extra (separados por espacio): +EXTRA_BINS="/usr/bin/strace /usr/bin/gdb" scripts/build-arje-initrd.sh +``` + +El script: + +1. Corre `cargo build --release` para `ente-zero` + los 14 shims compat. +2. Valida la seed con `seeds/validate.sh` (parse + `Card::validate`). +3. Stage en un tmpdir → copia binarios + crea mountpoints + escribe `/init`. +4. Empaqueta `find . | cpio -o -H newc | gzip -9`. + +Tamaño típico: ~15-25 MB descomprimido, 5-8 MB el `.cpio.gz`. + +## 3. Boot en QEMU + +Requisito: un kernel Linux + qemu-system-x86_64. + +### 3a. Con kernel del host + +```bash +# Default: usa /boot/vmlinuz-$(uname -r) +scripts/run-arje-qemu.sh + +# Con kernel explícito: +scripts/run-arje-qemu.sh out/arje.initrd.cpio.gz /boot/vmlinuz-6.6.0 +``` + +El script invoca: + +``` +qemu-system-x86_64 + -accel kvm (si /dev/kvm está, fallback tcg) + -m 1024 -smp 2 + -kernel + -initrd out/arje.initrd.cpio.gz + -append "rdinit=/init console=ttyS0,115200 panic=10" + -nographic -serial mon:stdio + -no-reboot +``` + +`rdinit=/init` le dice al kernel que el primer programa a ejecutar es +nuestro `/init`, no `/sbin/init`. `console=ttyS0,115200` redirige el log +del kernel + nuestro tracing a la serial → stdio del host. Salida con +`Ctrl-A X`. + +### 3b. Sin kernel del host + +Bajar un kernel mínimo (ej. Ubuntu cloud kernel): + +```bash +wget -O /tmp/vmlinuz \ + https://cloud-images.ubuntu.com/jammy/current/unpacked/jammy-server-cloudimg-amd64-vmlinuz-generic +scripts/run-arje-qemu.sh out/arje.initrd.cpio.gz /tmp/vmlinuz +``` + +O usar el kernel de Alpine (más liviano, ~10 MB): + +```bash +wget -O /tmp/vmlinuz \ + https://dl-cdn.alpinelinux.org/alpine/v3.20/releases/x86_64/netboot/vmlinuz-lts +``` + +### 3c. Override del cmdline + +`KERNEL_CMD="ente.log=debug"` se concatena al cmdline. Cosas útiles: + +| flag | efecto | +| -------------------------- | --------------------------------------------------- | +| `panic=10` | reboot 10 s tras kernel panic (debug) | +| `loglevel=7` | log del kernel hasta debug | +| `quiet` | silencia banner kernel | +| `RUST_LOG=trace` | (no se interpreta; usar env en `/init`) | +| `init=/sbin/ente-zero` | salta `/init`, ejecuta directo (no recomendado) | + +`ente-zero` lee `RUST_LOG` y `BRAHMAN_*` del env. Para setearlas, editar +`/init` antes de empaquetar, o agregar `-fw_cfg name=opt/foo,...` a qemu. + +### 3d. Smoke test esperado + +Con `seeds/arje-prod.card.json`: + +``` +ente-zero despierta como PID 1 +Tarjeta Semilla cargada y validada path=/ente/seed.card.json +bus interno escuchando path=/run/ente-bus.sock +brahman handshake escuchando (Unix) socket=/run/brahman-init.sock +brahman admin escuchando socket=/run/brahman-admin.sock +instanciando genesis seed=arje.seed.prod count=16 +Ente compat-logind encarnado pid=... +Ente compat-hostnamed encarnado pid=... +... +arje# ← shell en tty +``` + +Con `seeds/arje-minimal.card.json`: solo el shell. + +## 4. Boot en bare metal / VM persistente + +El mismo `.cpio.gz` sirve como initramfs estándar de Linux. Entrada +GRUB típica (`/etc/grub.d/40_custom` + `update-grub`): + +``` +menuentry "arje" { + linux /boot/vmlinuz-6.6.0 rdinit=/init console=tty1 panic=10 + initrd /boot/arje.initrd.cpio.gz +} +``` + +Copiar a `/boot/`: + +```bash +sudo cp out/arje.initrd.cpio.gz /boot/arje.initrd.cpio.gz +sudo update-grub +``` + +Para una VM (Proxmox, libvirt, etc.) basta con apuntar el "Direct Kernel +Boot" al vmlinuz y al initrd. En Proxmox: editar `/etc/pve/qemu-server/.conf`: + +``` +args: -kernel /boot/vmlinuz-6.6.0 -initrd /boot/arje.initrd.cpio.gz \ + -append "rdinit=/init console=ttyS0,115200 panic=10" +serial0: socket +``` + +### 4a. Sin pivot_root (initramfs es el rootfs final) + +El initrd actual **no hace pivot_root** — se queda como rootfs. Esto es +intencional: arje no asume nada del disco. Para persistencia, las Cards +hijas deben montar el FS de disco a demanda (ej. `genesis: [{ label: +"mount-data", payload: Native("/bin/mount", ["/dev/sda1", "/mnt"]), … }]`). + +Cuando necesites pivot_root a un FS real (instalación full-disk), agregar +un Ente que haga `switch_root` antes de instanciar el resto — pendiente +de implementar como `Capability::SwitchRoot`. + +## 5. Tarjeta Semilla — detalles + +### 5a. Seeds estándar + +| seed | uso | +| ----------------------------- | ----------------------------------------------------- | +| `seeds/arje-minimal.card.json` | PID 1 + 1 shell `/bin/sh`. Smoke test para QEMU. | +| `seeds/arje-prod.card.json` | Constelación completa: 14 compat shims + getty. | + +### 5b. Validación + +```bash +seeds/validate.sh seeds/arje-prod.card.json +``` + +El script carga la Card vía `ente_brain::load_card_file()` (que llama a +`brahman_card::Card::validate()`) y verifica que `ente-zero` la encarne +hasta `instanciando genesis`. + +### 5c. Customizar + +Estructura de un genesis-child: + +```json +{ + "schema_version": 1, + "id": "", + "label": "mi-servicio", + "provides": ["Journal"], + "requires": [], + "permissions": { + "networking": "loopback", + "filesystem": "read-write", + "ipc": { "allow": ["wit-v1"] }, + "processes": false + }, + "soma": { + "namespaces": { "mount": true, "pid": true, "net": false, ...}, + "rlimits": { "mem_bytes": 268435456 }, + "cgroup": { "path": "ente.slice/mi-servicio", "cpu_weight": 100 } + }, + "payload": { "Native": { "exec": "/usr/sbin/mi-bin", "argv": [], "envp": [] } }, + "supervision": { "Restart": { "initial": 100, "max": 30000 } }, + "lifecycle": "daemon", + "priority": "normal", + "flow": { "input": [], "output": [] }, + "genesis": [] +} +``` + +Validación clave (`brahman_card::Card::validate`): + +- `schema_version == 1`. +- `label` no vacío, ≤ 256 bytes. +- ULID válido (Crockford base32, **sin `I L O U`**). +- `provides ∩ requires == ∅`. +- `Payload::Native.exec` no vacío. +- `Payload::Wasm.module_sha256` no todo ceros. +- rlimits: `mem_bytes > 0 && < 1 TiB`, `nproc ∈ [1, 65535]`, `nofile ∈ [1, 1M]`. +- `cgroup.cpu_weight`, `io_weight ∈ [1, 10000]`. +- `flow.input` y `flow.output` con nombres únicos. +- Recursivo sobre `genesis`. + +## 6. Debugging + +### 6a. Tracing + +`ente-zero` usa `tracing-subscriber`. Default: `ente_zero=debug,info`. +Override con `RUST_LOG`: + +```bash +# En el initrd: editar /init antes de empaquetar +echo 'export RUST_LOG="trace"' >> wrapper +``` + +### 6b. Sockets de introspección (Unix, dentro del initrd) + +| socket | servicio | +| ---------------------------- | ------------------------------------------------- | +| `/run/ente-bus.sock` | bus interno (postcard, `BusRequest::Invoke/...`) | +| `/run/brahman-init.sock` | handshake brahman | +| `/run/brahman-admin.sock` | snapshots de sesiones + matches | +| `/run/ente-brain.sock` | introspección del cerebro | + +Conectar desde un Ente hijo: `socat - UNIX-CONNECT:/run/brahman-admin.sock`. +(El initrd debe tener `socat` o equivalente; agregalo con `EXTRA_BINS`.) + +### 6c. Snapshot / restore + +``` +ente-zero --checkpoint /ente/checkpoint.json # escribe al cerrar +ente-zero --restore /ente/checkpoint.json # reconstruye al boot +``` + +Snapshot adjunto del cerebro: `/ente/checkpoint.brain.json`. + +### 6d. Metrics + +``` +ente-zero --metrics-addr 127.0.0.1:9911 +``` + +Endpoint Prometheus desde dentro de la VM. Para exponerlo al host bajo +QEMU, agregar `-netdev user,hostfwd=tcp::9911-:9911 -device virtio-net,netdev=…`. + +### 6e. Modo DEV en host (sin PID 1) + +`ente-zero` detecta si su PID != 1 y entra en **DEV MODE**: no monta +kernel surface, no se vuelve subreaper, sale tras 4 s. Útil para +iterar Cards en el host: + +```bash +mkdir /tmp/arje-test && cp seeds/arje-minimal.card.json /tmp/arje-test/seed.card.json +cd /tmp/arje-test && target/release/ente-zero +``` + +--- + +## Apéndice — checklist pre-deploy + +- [ ] `cargo build --release -p ente-zero` sin warnings. +- [ ] `seeds/validate.sh seeds/arje-prod.card.json` → OK. +- [ ] `scripts/build-arje-initrd.sh` produce `out/arje.initrd.cpio.gz`. +- [ ] `scripts/run-arje-qemu.sh` arranca y muestra `Tarjeta Semilla cargada + y validada` + `instanciando genesis count=16` (o el count que toque). +- [ ] Si vas a bare metal: tener un kernel `vmlinuz` rescue (ej. Alpine + netboot) en `/boot/` por si arje no levanta. +- [ ] Para VMs Proxmox/libvirt: serial console habilitada para ver el + arranque sin display. diff --git a/scripts/build-arje-initrd.sh b/scripts/build-arje-initrd.sh new file mode 100755 index 0000000..fa4363b --- /dev/null +++ b/scripts/build-arje-initrd.sh @@ -0,0 +1,140 @@ +#!/usr/bin/env bash +# build-arje-initrd.sh — empaqueta ente-zero + shims compat + Tarjeta Semilla +# en un initramfs CPIO+gzip listo para arrancar bajo QEMU o como /init real. +# +# Layout del initrd resultante: +# /init → wrapper sh que exec /sbin/ente-zero +# /sbin/ente-zero → PID 1 +# /usr/sbin/ente-*-compat → shims systemd +# /usr/sbin/ente-echo, ente-policy-provider +# /ente/seed.card.json → Tarjeta Semilla +# /bin/{sh,ls,cat,...} → busybox o glibc-static (depende del flag) +# /dev, /proc, /sys, /run → puntos de montaje (ente-zero los monta) +# +# Uso: +# scripts/build-arje-initrd.sh [seed.card.json] [out.cpio.gz] +# +# seed default: seeds/arje-prod.card.json +# out default: out/arje.initrd.cpio.gz +# +# Env: +# BUSYBOX_BIN path a un busybox-static (default: $(which busybox)) +# EXTRA_BINS binarios extra a copiar, separados por espacio +# +# Requisitos: cpio, gzip, ldd (sólo si no usás busybox-static). + +set -euo pipefail + +SEED="${1:-seeds/arje-prod.card.json}" +OUT="${2:-out/arje.initrd.cpio.gz}" +BUSYBOX_BIN="${BUSYBOX_BIN:-$(command -v busybox 2>/dev/null || true)}" +EXTRA_BINS="${EXTRA_BINS:-}" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +cd "$REPO_DIR" + +if [ ! -f "$SEED" ]; then + echo "[build-initrd] seed no encontrada: $SEED" >&2 + exit 2 +fi + +# 1. Build release de ente-zero y todos los compat shims. +echo "[build-initrd] cargo build --release de ente-zero + shims" +cargo build --release \ + -p ente-zero \ + -p ente-echo \ + -p ente-logind-compat \ + -p ente-hostnamed-compat \ + -p ente-timedated-compat \ + -p ente-localed-compat \ + -p ente-journald-compat \ + -p ente-resolved-compat \ + -p ente-polkit-compat \ + -p ente-machined-compat \ + -p ente-systemd1-compat \ + -p ente-notify-compat \ + -p ente-timer-compat \ + -p ente-tmpfiles-compat \ + -p ente-binfmt-compat \ + -p ente-policy-provider + +# 2. Validar la seed. +if ! seeds/validate.sh "$SEED" >/dev/null 2>&1; then + echo "[build-initrd] seed inválida: $SEED" >&2 + exit 3 +fi + +# 3. Stage root del initrd. +STAGE="$(mktemp -d -t arje-initrd.XXXXXX)" +trap 'rm -rf "$STAGE"' EXIT +mkdir -p "$STAGE"/{bin,sbin,usr/sbin,etc,ente,proc,sys,dev,run,tmp,sys/fs/cgroup} + +# 4. Copiar binarios arje. +install -m 0755 target/release/ente-zero "$STAGE/sbin/ente-zero" +for b in ente-echo ente-policy-provider \ + ente-logind-compat ente-hostnamed-compat ente-timedated-compat \ + ente-localed-compat ente-journald-compat ente-resolved-compat \ + ente-polkit-compat ente-machined-compat ente-systemd1-compat \ + ente-notify-compat ente-timer-compat ente-tmpfiles-compat \ + ente-binfmt-compat; do + install -m 0755 "target/release/$b" "$STAGE/usr/sbin/$b" +done + +# 5. Userspace mínimo (sh, ls, mount, mkdir, ...). Dos rutas: +# (a) busybox-static apuntado por $BUSYBOX_BIN → 1 binario, todo simlink. +# (b) sin busybox → copiar /bin/sh + deps con ldd (libc dinámica). +if [ -n "$BUSYBOX_BIN" ] && [ -x "$BUSYBOX_BIN" ]; then + echo "[build-initrd] usando busybox-static: $BUSYBOX_BIN" + install -m 0755 "$BUSYBOX_BIN" "$STAGE/bin/busybox" + ( cd "$STAGE/bin" && for app in sh ls cat mount umount mkdir cp mv \ + echo grep sed awk ps kill sleep insmod modprobe poweroff reboot \ + sysctl dmesg ip ifconfig; do + ln -sf busybox "$app" + done ) +else + echo "[build-initrd] sin busybox — copiando /bin/sh + deps via ldd" + install -m 0755 /bin/sh "$STAGE/bin/sh" + copy_lib() { + local lib="$1" + [ -f "$lib" ] || return 0 + local dest="$STAGE${lib}" + mkdir -p "$(dirname "$dest")" + cp -L "$lib" "$dest" + } + for b in /bin/sh /bin/ls /bin/cat /bin/mount /bin/umount /bin/mkdir; do + [ -x "$b" ] || continue + cp -L "$b" "$STAGE${b}" + while read -r lib; do copy_lib "$lib"; done < <( + ldd "$b" 2>/dev/null | awk '{ for (i=1;i<=NF;i++) if ($i ~ /^\//) print $i }' + ) + done +fi + +# 6. Tarjeta Semilla. Path canónico en prod: /ente/seed.card.json +install -m 0644 "$SEED" "$STAGE/ente/seed.card.json" + +# 7. /init wrapper. El kernel pasa control a /init; nosotros invocamos +# ente-zero como PID 1 real con su env mínimo. +cat > "$STAGE/init" <<'EOF' +#!/bin/sh +# arje /init — kernel → este script → ente-zero (PID 1 lo hereda via exec) +export PATH=/usr/sbin:/usr/bin:/sbin:/bin +export RUST_LOG="${RUST_LOG:-ente_zero=info,brahman_handshake=info,info}" +# ente-zero monta /proc /sys /dev /sys/fs/cgroup él mismo. +exec /sbin/ente-zero +EOF +chmod 0755 "$STAGE/init" + +# 8. Binarios extra a vendorear. +if [ -n "$EXTRA_BINS" ]; then + for b in $EXTRA_BINS; do + [ -x "$b" ] || { echo "[build-initrd] EXTRA_BINS: $b no existe"; exit 4; } + install -m 0755 "$b" "$STAGE/usr/sbin/$(basename "$b")" + done +fi + +# 9. Empaquetar CPIO + gzip. Formato newc (estándar para Linux initramfs). +mkdir -p "$(dirname "$OUT")" +( cd "$STAGE" && find . -print0 | cpio -o -H newc --null --quiet ) | gzip -9 > "$OUT" +echo "[build-initrd] generado: $OUT ($(du -h "$OUT" | cut -f1))" diff --git a/scripts/run-arje-qemu.sh b/scripts/run-arje-qemu.sh new file mode 100755 index 0000000..31cb274 --- /dev/null +++ b/scripts/run-arje-qemu.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash +# run-arje-qemu.sh — bootea el initrd de arje bajo qemu-system-x86_64. +# +# Uso: +# scripts/run-arje-qemu.sh [initrd] [kernel] +# +# Defaults: +# initrd out/arje.initrd.cpio.gz +# kernel $(uname -r) o /boot/vmlinuz-* (primer match) +# +# Env: +# QEMU binario de qemu (default: qemu-system-x86_64) +# KERNEL_CMD extra cmdline kernel (se concatena al fijo) +# MEM RAM en MB (default: 1024) +# SMP CPUs virtuales (default: 2) +# ACCEL kvm|tcg (default: kvm si /dev/kvm existe) +# HEADLESS 1 → sin display (consola en stdio) +# +# El initrd contiene su propio /init → ente-zero corre como PID 1 real. + +set -euo pipefail + +INITRD="${1:-out/arje.initrd.cpio.gz}" +KERNEL="${2:-}" +QEMU="${QEMU:-qemu-system-x86_64}" +MEM="${MEM:-1024}" +SMP="${SMP:-2}" +KERNEL_CMD="${KERNEL_CMD:-}" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +cd "$REPO_DIR" + +if [ ! -f "$INITRD" ]; then + echo "[run-qemu] initrd no encontrado: $INITRD" >&2 + echo " Generalo primero con: scripts/build-arje-initrd.sh" >&2 + exit 2 +fi + +if [ -z "$KERNEL" ]; then + # En orden: /boot/vmlinuz-$(uname -r), primer vmlinuz-*, /boot/bzImage. + for cand in "/boot/vmlinuz-$(uname -r)" /boot/vmlinuz-* /boot/bzImage*; do + if [ -f "$cand" ]; then KERNEL="$cand"; break; fi + done +fi + +if [ -z "$KERNEL" ] || [ ! -f "$KERNEL" ]; then + echo "[run-qemu] kernel no encontrado." >&2 + echo " Pasalo como 2do arg o instalá linux-image-amd64." >&2 + echo " También podés bajar uno: " >&2 + echo " wget https://kernel.ubuntu.com/...vmlinuz" >&2 + exit 3 +fi + +# KVM si está disponible. +ACCEL="${ACCEL:-}" +if [ -z "$ACCEL" ]; then + if [ -e /dev/kvm ] && [ -r /dev/kvm ] && [ -w /dev/kvm ]; then + ACCEL="kvm" + else + ACCEL="tcg" + fi +fi + +DISPLAY_ARGS=() +if [ "${HEADLESS:-0}" = "1" ]; then + DISPLAY_ARGS+=( -nographic -serial mon:stdio ) + KERNEL_CMD="console=ttyS0,115200 $KERNEL_CMD" +else + DISPLAY_ARGS+=( -nographic -serial mon:stdio ) + KERNEL_CMD="console=ttyS0,115200 $KERNEL_CMD" +fi + +CMDLINE="rdinit=/init panic=10 loglevel=4 $KERNEL_CMD" + +echo "[run-qemu] kernel: $KERNEL" +echo "[run-qemu] initrd: $INITRD ($(du -h "$INITRD" | cut -f1))" +echo "[run-qemu] accel: $ACCEL" +echo "[run-qemu] mem: ${MEM}M, smp: $SMP" +echo "[run-qemu] cmdline: $CMDLINE" +echo "[run-qemu] (Ctrl-A X en stdio mode para salir)" + +exec "$QEMU" \ + -accel "$ACCEL" \ + -m "$MEM" -smp "$SMP" \ + -kernel "$KERNEL" \ + -initrd "$INITRD" \ + -append "$CMDLINE" \ + "${DISPLAY_ARGS[@]}" \ + -no-reboot diff --git a/seeds/arje-minimal.card.json b/seeds/arje-minimal.card.json new file mode 100644 index 0000000..33533a2 --- /dev/null +++ b/seeds/arje-minimal.card.json @@ -0,0 +1,62 @@ +{ + "schema_version": 1, + "id": "01J8YVKZP0M0M0M0M0M0M0M0M1", + "lineage": null, + "label": "arje.seed.minimal", + "provides": ["Spawn", "Journal"], + "requires": [], + "permissions": { + "networking": "none", + "filesystem": "read-write", + "ipc": { "allow": [] }, + "processes": true + }, + "soma": { + "namespaces": { + "mount": false, "pid": false, "net": false, + "uts": false, "ipc": false, "user": false, "cgroup": false + }, + "rlimits": { "mem_bytes": null, "nproc": null, "nofile": null }, + "cgroup": { "path": "ente.slice/zero", "cpu_weight": null, "io_weight": null }, + "cpu_affinity": null + }, + "payload": "Virtual", + "supervision": "OneShot", + "lifecycle": "daemon", + "priority": "critical", + "flow": { "input": [], "output": [] }, + "genesis": [ + { + "schema_version": 1, + "id": "01J8YVKZP0M0M0M0M0M0M0M0M2", + "lineage": null, + "label": "shell", + "provides": [], + "requires": [], + "permissions": { + "networking": "none", + "filesystem": "read-write", + "ipc": { "allow": [] }, + "processes": true + }, + "soma": { + "namespaces": { "mount": false, "pid": false, "net": false, "uts": false, "ipc": false, "user": false, "cgroup": false }, + "rlimits": { "mem_bytes": null, "nproc": null, "nofile": null }, + "cgroup": { "path": "ente.slice/shell", "cpu_weight": null, "io_weight": null }, + "cpu_affinity": null + }, + "payload": { + "Native": { + "exec": "/bin/sh", + "argv": ["-i"], + "envp": [["PATH", "/bin:/usr/bin"], ["TERM", "linux"], ["PS1", "arje# "]] + } + }, + "supervision": { "Restart": { "initial": 100, "max": 30000 } }, + "lifecycle": "daemon", + "priority": "high", + "flow": { "input": [], "output": [] }, + "genesis": [] + } + ] +} diff --git a/seeds/arje-prod.card.json b/seeds/arje-prod.card.json new file mode 100644 index 0000000..f18019e --- /dev/null +++ b/seeds/arje-prod.card.json @@ -0,0 +1,296 @@ +{ + "schema_version": 1, + "id": "01J8YVKZQ0M0M0M0M0M0M0M0M0", + "lineage": null, + "label": "arje.seed.prod", + "provides": ["Spawn", "Journal"], + "requires": [], + "permissions": { + "networking": "loopback", + "filesystem": "read-write", + "ipc": { "allow": ["wit-v1"] }, + "processes": true + }, + "soma": { + "namespaces": { + "mount": false, "pid": false, "net": false, + "uts": false, "ipc": false, "user": false, "cgroup": false + }, + "rlimits": { "mem_bytes": null, "nproc": null, "nofile": null }, + "cgroup": { "path": "ente.slice/zero", "cpu_weight": null, "io_weight": null }, + "cpu_affinity": null + }, + "payload": "Virtual", + "supervision": "OneShot", + "lifecycle": "daemon", + "priority": "critical", + "flow": { "input": [], "output": [] }, + "genesis": [ + { + "schema_version": 1, + "id": "01J8YVKZQ0M0M0M0M0M0M0M0M1", + "lineage": null, + "label": "tmpfiles-boot", + "provides": [], + "requires": [], + "permissions": { "networking": "none", "filesystem": "read-write", "ipc": { "allow": [] }, "processes": false }, + "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false}, + "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null}, + "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null}, + "cpu_affinity": null }, + "payload": { "Native": { "exec": "/usr/sbin/ente-tmpfiles-compat", "argv": ["--boot"], "envp": [] } }, + "supervision": "OneShot", + "lifecycle": "oneshot", + "priority": "critical", + "flow": { "input": [], "output": [] }, "genesis": [] + }, + { + "schema_version": 1, + "id": "01J8YVKZQ0M0M0M0M0M0M0M0M2", + "lineage": null, + "label": "binfmt-boot", + "provides": [], + "requires": [], + "permissions": { "networking": "none", "filesystem": "read-write", "ipc": { "allow": [] }, "processes": false }, + "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false}, + "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null}, + "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null}, + "cpu_affinity": null }, + "payload": { "Native": { "exec": "/usr/sbin/ente-binfmt-compat", "argv": [], "envp": [] } }, + "supervision": "OneShot", + "lifecycle": "oneshot", + "priority": "high", + "flow": { "input": [], "output": [] }, "genesis": [] + }, + { + "schema_version": 1, + "id": "01J8YVKZQ0M0M0M0M0M0M0M0H0", + "lineage": null, + "label": "compat-hostnamed", + "provides": [], "requires": [], + "permissions": { "networking": "loopback", "filesystem": "read-only", "ipc": { "allow": ["dbus-v1"] }, "processes": false }, + "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false}, + "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null}, + "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null}, + "cpu_affinity": null }, + "payload": { "Native": { "exec": "/usr/sbin/ente-hostnamed-compat", "argv": [], "envp": [] } }, + "supervision": { "Restart": { "initial": 100, "max": 30000 } }, + "lifecycle": "daemon", "priority": "normal", + "flow": { "input": [], "output": [] }, "genesis": [] + }, + { + "schema_version": 1, + "id": "01J8YVKZQ0M0M0M0M0M0M0M0T0", + "lineage": null, + "label": "compat-timedated", + "provides": [], "requires": [], + "permissions": { "networking": "loopback", "filesystem": "read-only", "ipc": { "allow": ["dbus-v1"] }, "processes": false }, + "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false}, + "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null}, + "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null}, + "cpu_affinity": null }, + "payload": { "Native": { "exec": "/usr/sbin/ente-timedated-compat", "argv": [], "envp": [] } }, + "supervision": { "Restart": { "initial": 100, "max": 30000 } }, + "lifecycle": "daemon", "priority": "normal", + "flow": { "input": [], "output": [] }, "genesis": [] + }, + { + "schema_version": 1, + "id": "01J8YVKZQ0M0M0M0M0M0M0M0X0", + "lineage": null, + "label": "compat-localed", + "provides": [], "requires": [], + "permissions": { "networking": "loopback", "filesystem": "read-only", "ipc": { "allow": ["dbus-v1"] }, "processes": false }, + "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false}, + "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null}, + "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null}, + "cpu_affinity": null }, + "payload": { "Native": { "exec": "/usr/sbin/ente-localed-compat", "argv": [], "envp": [] } }, + "supervision": { "Restart": { "initial": 100, "max": 30000 } }, + "lifecycle": "daemon", "priority": "normal", + "flow": { "input": [], "output": [] }, "genesis": [] + }, + { + "schema_version": 1, + "id": "01J8YVKZQ0M0M0M0M0M0M0M0J0", + "lineage": null, + "label": "compat-journald", + "provides": ["Journal"], "requires": [], + "permissions": { "networking": "none", "filesystem": "read-write", "ipc": { "allow": ["dbus-v1"] }, "processes": false }, + "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false}, + "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null}, + "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null}, + "cpu_affinity": null }, + "payload": { "Native": { "exec": "/usr/sbin/ente-journald-compat", "argv": [], "envp": [] } }, + "supervision": { "Restart": { "initial": 100, "max": 30000 } }, + "lifecycle": "daemon", "priority": "high", + "flow": { "input": [], "output": [] }, "genesis": [] + }, + { + "schema_version": 1, + "id": "01J8YVKZQ0M0M0M0M0M0M0M0R0", + "lineage": null, + "label": "compat-resolved", + "provides": [], "requires": [], + "permissions": { "networking": "outbound", "filesystem": "read-write", "ipc": { "allow": ["dbus-v1"] }, "processes": false }, + "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false}, + "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null}, + "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null}, + "cpu_affinity": null }, + "payload": { "Native": { "exec": "/usr/sbin/ente-resolved-compat", "argv": [], "envp": [] } }, + "supervision": { "Restart": { "initial": 100, "max": 30000 } }, + "lifecycle": "daemon", "priority": "normal", + "flow": { "input": [], "output": [] }, "genesis": [] + }, + { + "schema_version": 1, + "id": "01J8YVKZQ0M0M0M0M0M0M0M0P0", + "lineage": null, + "label": "compat-polkit", + "provides": [], "requires": [], + "permissions": { "networking": "loopback", "filesystem": "read-only", "ipc": { "allow": ["dbus-v1"] }, "processes": false }, + "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false}, + "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null}, + "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null}, + "cpu_affinity": null }, + "payload": { "Native": { "exec": "/usr/sbin/ente-polkit-compat", "argv": [], "envp": [] } }, + "supervision": { "Restart": { "initial": 100, "max": 30000 } }, + "lifecycle": "daemon", "priority": "normal", + "flow": { "input": [], "output": [] }, "genesis": [] + }, + { + "schema_version": 1, + "id": "01J8YVKZQ0M0M0M0M0M0M0M0Q0", + "lineage": null, + "label": "policy-provider", + "provides": [], "requires": [], + "permissions": { "networking": "none", "filesystem": "read-only", "ipc": { "allow": ["wit-v1"] }, "processes": false }, + "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false}, + "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null}, + "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null}, + "cpu_affinity": null }, + "payload": { "Native": { "exec": "/usr/sbin/ente-policy-provider", "argv": [], "envp": [] } }, + "supervision": { "Restart": { "initial": 100, "max": 30000 } }, + "lifecycle": "daemon", "priority": "normal", + "flow": { "input": [], "output": [] }, "genesis": [] + }, + { + "schema_version": 1, + "id": "01J8YVKZQ0M0M0M0M0M0M0M0M0", + "lineage": null, + "label": "compat-machined", + "provides": [], "requires": [], + "permissions": { "networking": "loopback", "filesystem": "read-only", "ipc": { "allow": ["dbus-v1"] }, "processes": false }, + "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false}, + "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null}, + "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null}, + "cpu_affinity": null }, + "payload": { "Native": { "exec": "/usr/sbin/ente-machined-compat", "argv": [], "envp": [] } }, + "supervision": { "Restart": { "initial": 100, "max": 30000 } }, + "lifecycle": "daemon", "priority": "normal", + "flow": { "input": [], "output": [] }, "genesis": [] + }, + { + "schema_version": 1, + "id": "01J8YVKZQ0M0M0M0M0M0M0M0S0", + "lineage": null, + "label": "compat-systemd1", + "provides": [], "requires": [], + "permissions": { "networking": "loopback", "filesystem": "read-write", "ipc": { "allow": ["dbus-v1"] }, "processes": false }, + "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false}, + "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null}, + "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null}, + "cpu_affinity": null }, + "payload": { "Native": { "exec": "/usr/sbin/ente-systemd1-compat", "argv": [], "envp": [] } }, + "supervision": { "Restart": { "initial": 100, "max": 30000 } }, + "lifecycle": "daemon", "priority": "high", + "flow": { "input": [], "output": [] }, "genesis": [] + }, + { + "schema_version": 1, + "id": "01J8YVKZQ0M0M0M0M0M0M0M0N0", + "lineage": null, + "label": "compat-notify", + "provides": [], "requires": [], + "permissions": { "networking": "none", "filesystem": "read-write", "ipc": { "allow": ["dbus-v1"] }, "processes": false }, + "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false}, + "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null}, + "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null}, + "cpu_affinity": null }, + "payload": { "Native": { "exec": "/usr/sbin/ente-notify-compat", "argv": [], "envp": [] } }, + "supervision": { "Restart": { "initial": 100, "max": 30000 } }, + "lifecycle": "daemon", "priority": "normal", + "flow": { "input": [], "output": [] }, "genesis": [] + }, + { + "schema_version": 1, + "id": "01J8YVKZQ0M0M0M0M0M0M0M0Y0", + "lineage": null, + "label": "compat-logind", + "provides": ["LegacyLogind"], "requires": [], + "permissions": { "networking": "loopback", "filesystem": "read-write", "ipc": { "allow": ["dbus-v1"] }, "processes": false }, + "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false}, + "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null}, + "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null}, + "cpu_affinity": null }, + "payload": { "Native": { "exec": "/usr/sbin/ente-logind-compat", "argv": [], "envp": [] } }, + "supervision": { "Restart": { "initial": 100, "max": 30000 } }, + "lifecycle": "daemon", "priority": "high", + "flow": { "input": [], "output": [] }, "genesis": [] + }, + { + "schema_version": 1, + "id": "01J8YVKZQ0M0M0M0M0M0M0M0Z0", + "lineage": null, + "label": "compat-timer", + "provides": [], "requires": [], + "permissions": { "networking": "none", "filesystem": "read-write", "ipc": { "allow": [] }, "processes": true }, + "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false}, + "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null}, + "cgroup": {"path":"ente.slice/compat","cpu_weight":null,"io_weight":null}, + "cpu_affinity": null }, + "payload": { "Native": { "exec": "/usr/sbin/ente-timer-compat", "argv": [], "envp": [] } }, + "supervision": { "Restart": { "initial": 1000, "max": 60000 } }, + "lifecycle": "daemon", "priority": "low", + "flow": { "input": [], "output": [] }, "genesis": [] + }, + { + "schema_version": 1, + "id": "01J8YVKZQ0M0M0M0M0M0M0M0E0", + "lineage": null, + "label": "echo-smoke", + "provides": [], "requires": [], + "permissions": { "networking": "none", "filesystem": "read-only", "ipc": { "allow": ["wit-v1"] }, "processes": false }, + "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false}, + "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null}, + "cgroup": {"path":"ente.slice/test","cpu_weight":null,"io_weight":null}, + "cpu_affinity": null }, + "payload": { "Native": { "exec": "/usr/sbin/ente-echo", "argv": [], "envp": [] } }, + "supervision": { "Restart": { "initial": 200, "max": 30000 } }, + "lifecycle": "daemon", "priority": "low", + "flow": { "input": [], "output": [] }, "genesis": [] + }, + { + "schema_version": 1, + "id": "01J8YVKZQ0M0M0M0M0M0M0M0G0", + "lineage": null, + "label": "getty-tty1", + "provides": [], "requires": [], + "permissions": { "networking": "none", "filesystem": "read-write", "ipc": { "allow": [] }, "processes": true }, + "soma": { "namespaces": {"mount":false,"pid":false,"net":false,"uts":false,"ipc":false,"user":false,"cgroup":false}, + "rlimits": {"mem_bytes":null,"nproc":null,"nofile":null}, + "cgroup": {"path":"ente.slice/getty","cpu_weight":null,"io_weight":null}, + "cpu_affinity": null }, + "payload": { + "Native": { + "exec": "/bin/sh", + "argv": ["-i"], + "envp": [["PATH", "/usr/sbin:/usr/bin:/sbin:/bin"], ["TERM", "linux"], ["PS1", "arje# "]] + } + }, + "supervision": { "Restart": { "initial": 100, "max": 30000 } }, + "lifecycle": "daemon", "priority": "high", + "flow": { "input": [], "output": [] }, "genesis": [] + } + ] +} diff --git a/seeds/validate.sh b/seeds/validate.sh new file mode 100755 index 0000000..edb2de7 --- /dev/null +++ b/seeds/validate.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# Valida una Tarjeta Semilla pasándola por el parser+validate de brahman-card. +# Uso: seeds/validate.sh seeds/arje-prod.card.json +# +# Método: copiamos la Card como `seed.card.json` en un cwd vacío y corremos +# ente-zero en dev-mode 4 s. Si carga, valida, e instancia genesis, la +# Card es estructuralmente correcta — los binarios de los hijos pueden +# faltar en el host (vivirán en /usr/sbin del initrd). + +set -euo pipefail + +if [ "$#" -ne 1 ]; then + echo "uso: $0 " >&2 + exit 2 +fi + +SEED="$(realpath "$1")" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +BIN="$REPO_DIR/target/release/ente-zero" + +if [ ! -x "$BIN" ]; then + echo "[validate] compilando ente-zero (release)…" + (cd "$REPO_DIR" && cargo build --quiet --release -p ente-zero) +fi + +SCRATCH="$(mktemp -d -t arje-validate.XXXXXX)" +trap 'rm -rf "$SCRATCH"' EXIT +cp "$SEED" "$SCRATCH/seed.card.json" + +cd "$SCRATCH" +timeout 5 "$BIN" 2>&1 | tee /tmp/arje-validate.log | \ + grep -E "Tarjeta Semilla cargada|semilla inválida|JSON no contiene|Caused by" | head -5 + +if grep -q "Tarjeta Semilla cargada y validada" /tmp/arje-validate.log; then + echo "[validate] OK: $SEED" + exit 0 +fi +echo "[validate] FALLÓ: $SEED" >&2 +exit 1