feat: mirada standalone — compositor Wayland + WM sobre Llimphi (build magro)
1use super::*;
2
3// =============================================================================
4// Fase 67 :: la CONCESION DE CAPACIDAD — "que binario puede hacer que", firmado
5// -----------------------------------------------------------------------------
6// Hoy los permisos de una app viven en su `EntradaApp` del manifiesto: el
7// manifiesto firmado dice "el bytecode X corre con permisos P". El binding es
8// tan fuerte como el manifiesto — re-firmar un manifiesto nuevo basta para
9// darle al MISMO binario permisos distintos. La concesion eleva ese binding a
10// un hecho INDEPENDIENTE del manifiesto: una firma Ed25519 de una llave del
11// `AGORA_AUTH_RING` sobre el par `(hash_bytecode, permisos)`. La firma viaja
12// con el binario y NINGUN manifiesto puede escalar un binario mas alla de lo
13// que su concesion autoriza —el kernel toma la INTERSECCION, ver
14// [`permisos_efectivos`]—. Gemelo estructural de [`ManifiestoFirmado`]: la
15// verificacion comparte el camino Ring 0 zero-alloc de `ed25519-compact`, pero
16// el mensaje firmado es [`mensaje_capacidad`], no el hash pelado.
17// =============================================================================
18
19/// Una concesion de capacidad firmada: liga inmutablemente un bytecode (por su
20/// hash BLAKE3) a un bitfield de permisos, respaldada por la firma de una
21/// identidad soberana. Es un objeto del grafo (direccionado por contenido) que
22/// un `EntradaApp` referencia; el kernel la verifica contra el `AGORA_AUTH_RING`
23/// antes de enlazar capacidad alguna.
24#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
25pub struct ConcesionCapacidad {
26 /// Hash BLAKE3 del objeto-bytecode WASM al que esta concesion aplica. La
27 /// firma lo cubre: una concesion para el bytecode X jamas vale para Y.
28 pub bytecode: Hash,
29 /// Bitfield de permisos que esta concesion AUTORIZA para ese bytecode (ver
30 /// [`Permisos`] y las constantes `PERMISO_*`). Subir un bit invalida la firma.
31 pub permisos: Permisos,
32 /// Llave publica Ed25519 de quien concede. El kernel exige que habite el
33 /// `AGORA_AUTH_RING` antes de gastar un ciclo en criptografia.
34 pub autor: AgoraId,
35 /// Firma Ed25519 sobre [`mensaje_capacidad`]`(bytecode, permisos)`.
36 #[serde(with = "BigArray")]
37 pub firma: Firma,
38}
39
40impl ConcesionCapacidad {
41 /// Serializa la concesion a `postcard` — la carga util del objeto del grafo
42 /// que la aloja.
43 pub fn serializar(&self) -> Result<Vec<u8>, &'static str> {
44 postcard::to_allocvec(self)
45 .map_err(|_| "concesion_capacidad :: serializacion fallida")
46 }
47
48 /// Reconstruye una concesion desde su forma binaria. Tolera bytes
49 /// sobrantes tras la estructura — el relleno del registro.
50 pub fn deserializar(bytes: &[u8]) -> Result<ConcesionCapacidad, &'static str> {
51 postcard::take_from_bytes::<ConcesionCapacidad>(bytes)
52 .map(|(c, _)| c)
53 .map_err(|_| "concesion_capacidad :: deserializacion fallida")
54 }
55}
56
57// =============================================================================
58// Overlay de revocacion del plano de CONTROL (SDD-rotacion-revocacion §4)
59// -----------------------------------------------------------------------------
60// El AGORA_AUTH_RING del kernel es `const` en `.rodata`: rotar el ancla = reflash
61// deliberado. Pero entre reflasheos una clave soberana puede filtrarse, y esperar
62// al re-forjado deja una ventana abierta. El overlay la cierra: un objeto del
63// grafo, anclado por el manifiesto (`Manifiesto::overlay_revocacion`), que lista
64// revocaciones firmadas M-of-N por el RESTO del anillo. El kernel lo lee FRESH en
65// el arranque y deniega en `autor_en_anillo` toda clave del anillo revocada.
66//
67// Tipos `no_std + alloc`: el kernel los deserializa (postcard) y los verifica con
68// `claves::verificar_revocacion` sobre el canonico de `mensaje_revocacion_clave`.
69// El productor host-side (`agora-cli wawa revocar`) emite el mismo wire.
70//
71// TIEMPO: el kernel hoy lleva ticks PIT, no wall-clock. Aplica la revocacion
72// mientras este ANCLADA (fail-closed, deny-wins); `vence_en` entra en el canonico
73// firmado pero la auto-caducidad temporal espera un RTC. Des-revocar = anclar un
74// overlay nuevo sin esa entrada (gemelo de mover el puntero de `configuracion`).
75// =============================================================================
76
77/// Version del format del [`OverlayRevocacion`] serializado.
78pub const VERSION_OVERLAY: u32 = 1;
79
80/// Una firma individual dentro de una [`RevocacionFirmada`]: la pubkey del
81/// firmante y su firma Ed25519 sobre el canonico de la revocacion. Espejo
82/// minimo de `agora_core::SingleSig` (sin el `IdentityId` redundante: el kernel
83/// re-deriva la autoridad comparando la pubkey contra el `AGORA_AUTH_RING`).
84#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
85pub struct FirmaRevocacion {
86 /// Clave publica Ed25519 del firmante (un miembro del anillo, en control).
87 pub autor: AgoraId,
88 /// Firma Ed25519 sobre [`mensaje_revocacion_clave`]`(objetivo, motivo,
89 /// emitida_en, vence_en)`.
90 #[serde(with = "BigArray")]
91 pub firma: Firma,
92}
93
94/// Una revocacion de clave firmada por un quorum, en forma de wire para el
95/// overlay. Espejo de `agora_core::Revocation` aplanado para el kernel: el
96/// `motivo` es el discriminante estable de `RevReason` (0=Compromised,
97/// 1=Retired, 2=Superseded) y `firmantes` es la multifirma desnuda.
98#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
99pub struct RevocacionFirmada {
100 /// La clave que se revoca (en el plano de control, una del anillo).
101 pub objetivo: AgoraId,
102 /// Motivo (entra en el canonico firmado): 0=Compromised, 1=Retired, 2=Superseded.
103 pub motivo: u8,
104 /// Segundos UNIX desde cuando rige.
105 pub emitida_en: u64,
106 /// `None` ⇒ permanente; `Some(t)` ⇒ suspension hasta `t` (auto-caducidad
107 /// pendiente de RTC en el kernel — ver nota de tiempo arriba).
108 pub vence_en: Option<u64>,
109 /// Las firmas del quorum autorizador.
110 pub firmantes: Vec<FirmaRevocacion>,
111}
112
113/// El overlay de revocacion: la lista de revocaciones que el kernel consulta al
114/// arrancar. Objeto del grafo direccionado por contenido; el manifiesto guarda
115/// su hash en `overlay_revocacion`.
116#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Default)]
117pub struct OverlayRevocacion {
118 /// Version del format — debe ser [`VERSION_OVERLAY`].
119 pub version: u32,
120 /// Las revocaciones vigentes. El kernel aplica las que apunten a un slot del
121 /// anillo y reunan el quorum; ignora el resto (no son su jurisdiccion).
122 pub revocaciones: Vec<RevocacionFirmada>,
123}
124
125impl OverlayRevocacion {
126 /// Serializa el overlay a `postcard` — la carga util del objeto del grafo
127 /// que lo aloja.
128 pub fn serializar(&self) -> Result<Vec<u8>, &'static str> {
129 postcard::to_allocvec(self).map_err(|_| "overlay_revocacion :: serializacion fallida")
130 }
131
132 /// Reconstruye un overlay desde su forma binaria. Rechaza una version de
133 /// format desconocida en lugar de malinterpretarla. Tolera bytes sobrantes
134 /// tras la estructura — el relleno del registro.
135 pub fn deserializar(bytes: &[u8]) -> Result<OverlayRevocacion, &'static str> {
136 let (overlay, _) = postcard::take_from_bytes::<OverlayRevocacion>(bytes)
137 .map_err(|_| "overlay_revocacion :: deserializacion fallida")?;
138 if overlay.version != VERSION_OVERLAY {
139 return Err("overlay_revocacion :: version de format desconocida");
140 }
141 Ok(overlay)
142 }
143}
144
145// =============================================================================
146// Fase 37 :: el sello criptografico del CUADERNO SOBERANO
147// -----------------------------------------------------------------------------
148// La integridad de un cuaderno —un nodo del grafo cuyo payload es
149// `Vec<CeldaWawa>` (Fase 43, modelo unificado)— se proteje en dos planos:
150//
151// * Localmente, el direccionamiento por contenido garantiza que un
152// bit alterado en cualquier celda cambia el hash del cuaderno
153// —y ese hash es la identidad del nodo en el almacen—.
154// * En la red capa-2 (Akasha), eso no basta: un peer hostil puede
155// reescribir el cuaderno entero y reanunciarlo con su propio hash.
156// Para que el sistema reconozca un cuaderno como SOBERANO del
157// operador local, el peer ha de adjuntar una firma Ed25519 del
158// cuaderno_raiz_hash producida con la clave privada que pertenece
159// a la `AGORA_PUBLIC_KEY_LOCAL` empotrada en el binario del kernel.
160//
161// Gemelo estructural de `ManifiestoFirmado`: la verificacion comparte
162// el camino Ring 0 zero-alloc de `ed25519-compact`.
163// =============================================================================
164
165/// Sobre criptografico de un cuaderno: vincula su `hash` con un autor y
166/// una firma Ed25519. Sin este sobre, un cuaderno es solo un nodo mas
167/// del grafo — con el sobre, queda anclado como SOBERANO al usuario
168/// que firmo, y el kernel lo distingue de cualquier otro nodo cuaderno
169/// que viaje por la red.
170#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
171pub struct CuadernoFirmado {
172 /// Hash BLAKE3 del cuaderno propuesto. El payload del cuaderno es
173 /// `Vec<CeldaWawa>` serializado con postcard; este hash es el
174 /// resumen criptografico que va a engrapar la firma.
175 pub cuaderno_raiz_hash: Hash,
176 /// Llave publica Ed25519 del autor. El kernel la compara contra
177 /// `AGORA_PUBLIC_KEY_LOCAL` antes de gastar un ciclo en criptografia
178 /// — un autor ajeno cae con `CapacidadInsuficiente`.
179 pub autor: AgoraId,
180 /// Firma Ed25519 sobre los 32 bytes de `cuaderno_raiz_hash`.
181 #[serde(with = "BigArray")]
182 pub firma: Firma,
183}
184
185impl CuadernoFirmado {
186 /// Serializa el sobre a su forma binaria `postcard`. La forma cruda
187 /// ocupa 32 + 32 + 64 = 128 bytes; postcard agrega un preludio
188 /// minusculo (longitudes varint) que mantiene el sobre bajo 140 B.
189 pub fn serializar(&self) -> Result<Vec<u8>, &'static str> {
190 postcard::to_allocvec(self)
191 .map_err(|_| "cuaderno_firmado :: serializacion fallida")
192 }
193
194 /// Reconstruye un sobre desde su forma binaria. Tolera bytes
195 /// sobrantes tras la estructura — el relleno del registro o el
196 /// padding del payload del syscall.
197 pub fn deserializar(bytes: &[u8]) -> Result<CuadernoFirmado, &'static str> {
198 postcard::take_from_bytes::<CuadernoFirmado>(bytes)
199 .map(|(cf, _)| cf)
200 .map_err(|_| "cuaderno_firmado :: deserializacion fallida")
201 }
202}
203
204/// Una entrada del historial de un canal: una raiz de manifiesto, el instante
205/// en que el autor la propuso, y la firma Ed25519 con la que el autor la
206/// respalda. La firma se calcula sobre [`mensaje_a_firmar`].
207#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
208pub struct RaizFirmada {
209 /// Instante en que el autor propuso esta raiz, segundos desde UNIX epoch.
210 /// Un receptor desconfia de raices con timestamp futuro mas alla de un
211 /// margen razonable —proteccion barata contra anuncios envenenados—.
212 pub timestamp: u64,
213 /// El hash del [`Manifiesto`] que esta raiz inaugura. Re-anclar el
214 /// superbloque a este hash es, literalmente, "actualizar a esta version".
215 pub raiz_manifiesto: Hash,
216 /// La firma Ed25519 del autor del canal sobre [`mensaje_a_firmar`].
217 /// `serde` no derivara `Deserialize` para `[u8; 64]` sin ayuda —su soporte
218 /// directo se detiene en 32 bytes—; `serde-big-array` cierra ese hueco.
219 #[serde(with = "BigArray")]
220 pub firma: Firma,
221}
222