tawasuyu

tawasuyu/brahman

Rust 307TOML 110JSON 14Markdown 113965 Definiciones

⎇ 53dbdf0f1deea10d6fc567ea2b43e1e75d6d60cd crates/core/ente-card/schema/card.k 178 líneas · 7.2 KB Historia crudo

chore: monorepo inicial con arje + minga + yahweh absorbidos 53dbdf0f1 · Sergio ·

1# ============================================================================
2# card.k — REFERENCE ONLY. NOT LOADED.
3#
4# La validación canónica de EntityCard vive en Rust:
5#   crates/ente-card/src/lib.rs :: EntityCard::validate()
6# El loader (crates/ente-brain/src/loader.rs) sólo acepta JSON.
7#
8# Este archivo se conserva como notas de diseño legibles para humanos sobre
9# las invariantes que `validate()` debe garantizar. Si modificas el shape
10# en Rust, sincroniza este archivo a mano (o reemplázalo por JSON Schema
11# generado vía `schemars`).
12# ============================================================================
13
14# ---------- Identidad ----------
15
16schema EntityCard:
17    """Tarjeta de Identidad. Inmutable: cambios = nueva Card con nuevo id."""
18    schema_version: int = 1
19    id: str                              # Ulid (26 chars, Crockford base32)
20    lineage?: str                        # parent Ulid; None = Ente raíz
21    label: str                           # legible, no es identificador
22    provides: [Capability] = []          # contrato hacia el grafo
23    requires: [Capability] = []          # contrato del grafo hacia el Ente
24    soma: SomaSpec                       # cuerpo: aislamiento + recursos
25    payload: Payload                     # cómo encarnar (Wasm/Native/Virtual)
26    supervision: Supervision             # política tras muerte
27    genesis?: [EntityCard] = []          # hijos a instanciar al encarnar
28
29    check:
30        schema_version == 1, "schema version no soportada"
31        len(label) > 0, "label vacío"
32        len(id) == 26, "id debe ser Ulid (26 caracteres)"
33        # Auto-dependencia: una capacidad no puede estar en requires y provides
34        all c in requires { c not in provides }, "self-dependency: ${c}"
35
36
37# ---------- Capacidades (typed enum) ----------
38
39# KCL no tiene sum types nativos; usamos tagged union: `kind` + campos opcionales
40# que sólo aplican según el kind. Las invariantes en `check:` aseguran consistencia.
41schema Capability:
42    """Capacidad tipada del fractal. NUNCA usar strings libres."""
43    kind: "FilesystemRoot" | "KernelNetlink" | "Endpoint" | "LegacyLogind" | "Device" | "Spawn" | "Journal"
44    netlink_family?: "Uevent" | "Route" | "Generic" | "Audit"
45    endpoint_interface?: str             # 32-char hex (UUID 16 bytes)
46    endpoint_version?: int
47    device_class?: "Block" | "Tty" | "Input" | "Drm" | "Net" | "Hidraw"
48
49    check:
50        kind != "KernelNetlink" or netlink_family is not None, \
51            "KernelNetlink requiere netlink_family"
52        kind != "Endpoint" or (endpoint_interface is not None and endpoint_version is not None), \
53            "Endpoint requiere interface + version"
54        kind != "Endpoint" or len(endpoint_interface) == 32, \
55            "endpoint_interface debe ser hex de 32 chars"
56        kind != "Device" or device_class is not None, \
57            "Device requiere device_class"
58
59
60# ---------- Soma: cuerpo + restricciones de recursos ----------
61
62schema SomaSpec:
63    """Aislamiento + recursos. Validados por KCL antes de tocar el kernel."""
64    namespaces: NamespaceSet = NamespaceSet {}
65    rlimits: ResourceLimits = ResourceLimits {}
66    cgroup: CgroupSpec = CgroupSpec {}
67    cpu_affinity?: [int]                 # CPU pinning
68
69    check:
70        cpu_affinity is None or all c in cpu_affinity { c >= 0 and c < 1024 }, \
71            "cpu_affinity fuera de rango [0, 1024)"
72
73
74schema NamespaceSet:
75    mount: bool = False
76    pid: bool = False
77    net: bool = False
78    uts: bool = False
79    ipc: bool = False
80    user: bool = False
81    cgroup: bool = False
82
83
84schema ResourceLimits:
85    """Restricciones nativas validadas en KCL — el kernel sólo ve valores sanos."""
86    mem_bytes?: int                      # RLIMIT_AS
87    nproc?: int                          # RLIMIT_NPROC
88    nofile?: int                         # RLIMIT_NOFILE
89    energy_budget_mw?: int               # presupuesto energético (futuro)
90
91    check:
92        mem_bytes is None or mem_bytes > 0, "mem_bytes debe ser positivo"
93        mem_bytes is None or mem_bytes <= 1099511627776, "mem_bytes > 1 TiB sospechoso"
94        nproc is None or (nproc > 0 and nproc <= 65535), "nproc fuera de rango"
95        nofile is None or (nofile > 0 and nofile <= 1048576), "nofile fuera de rango"
96        energy_budget_mw is None or energy_budget_mw > 0, "energy_budget_mw debe ser positivo"
97
98
99schema CgroupSpec:
100    """Cgroup v2: path + weights. cpu_weight 1..10000 según kernel docs."""
101    path: str = ""
102    cpu_weight?: int
103    io_weight?: int
104
105    check:
106        cpu_weight is None or (cpu_weight >= 1 and cpu_weight <= 10000), \
107            "cpu_weight 1..10000"
108        io_weight is None or (io_weight >= 1 and io_weight <= 10000), \
109            "io_weight 1..10000"
110
111
112# ---------- Payload: tagged union de cómo encarnar ----------
113
114schema Payload:
115    """Una variante por Card. Set exactly one of: Wasm, Native, Virtual, Legacy."""
116    kind: "Wasm" | "Native" | "Virtual" | "Legacy"
117    # Wasm
118    module_sha256?: str                  # hex 64 chars
119    entry?: str
120    # Native / Legacy
121    exec?: str
122    argv?: [str] = []
123    envp?: [{str: str}] = []
124    # Legacy
125    fakes?: ["SystemdLogind" | "SystemdHostnamed" | "SystemdNotify"] = []
126
127    check:
128        kind != "Wasm" or (module_sha256 is not None and entry is not None), \
129            "Wasm requiere module_sha256 + entry"
130        kind != "Wasm" or len(module_sha256) == 64, "module_sha256 debe ser hex de 64 chars"
131        kind != "Native" or exec is not None, "Native requiere exec"
132        kind != "Legacy" or exec is not None, "Legacy requiere exec"
133
134
135# ---------- Supervision ----------
136
137schema Supervision:
138    kind: "Restart" | "OneShot" | "Delegate"
139    initial_ms?: int                     # ms — backoff inicial para Restart
140    max_ms?: int                         # ms — backoff máximo
141
142    check:
143        kind != "Restart" or (initial_ms is not None and max_ms is not None), \
144            "Restart requiere initial_ms + max_ms"
145        initial_ms is None or initial_ms >= 0, "initial_ms negativo"
146        max_ms is None or max_ms >= initial_ms or max_ms is None, \
147            "max_ms < initial_ms es contradictorio"
148
149
150# ============================================================================
151# Herencia: EnteWeb hereda de EnteBase con campos pre-rellenados.
152# ============================================================================
153
154schema EnteBase(EntityCard):
155    """Base para Entes managed: declara Spawn provider y Journal por defecto."""
156    schema_version = 1
157    supervision = Supervision {kind = "Restart", initial_ms = 100, max_ms = 30000}
158    soma = SomaSpec {
159        rlimits = ResourceLimits {nofile = 4096}
160        cgroup = CgroupSpec {path = "ente.slice/managed", cpu_weight = 100}
161    }
162
163
164schema EnteWeb(EnteBase):
165    """Hereda EnteBase, declara endpoint + cap LegacyLogind como ejemplo."""
166    provides = [
167        Capability {kind = "Journal"}
168        Capability {
169            kind = "Endpoint"
170            endpoint_interface = "deadbeefcafe1234deadbeefcafe1234"
171            endpoint_version = 1
172        }
173    ]
174    soma = SomaSpec {
175        namespaces = NamespaceSet {net = True, mount = True, pid = True}
176        rlimits = ResourceLimits {nofile = 16384, mem_bytes = 536870912}  # 512 MiB
177        cgroup = CgroupSpec {path = "ente.slice/web", cpu_weight = 200, io_weight = 100}
178    }