feat(sandokan-daemon): B1.3 — DaemonEngine + protocolo wire

DaemonEngine: implementación del trait Engine que delega a otro proceso
vía Unix socket. Materializa el patrón horizontal de sandokan (el
binario que arranca primero expone el engine; los demás se le suman).

- protocol.rs — DaemonRequest/DaemonResponse (espejan los métodos de
  Engine) + framing postcard length-prefixed (u32 LE + bytes), con
  MAX_FRAME 16 MiB defensivo.
- client.rs — DaemonEngine: stateless, un round-trip por llamada;
  is_reachable() para el probe de auto().
- server.rs — serve(engine, socket): envuelve cualquier Engine, una
  task por conexión, multi-request por conexión.

EngineError ahora es Serialize/Deserialize (viaja por el wire);
NotFound se propaga tipado a través del socket.

1 test de integración: roundtrip real DaemonEngine ↔ serve ↔ LocalEngine
(list vacío + NotFound propagado). cargo check --workspace verde.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-20 14:04:22 +00:00
parent cba3a9dd6e
commit b7d9d7abd9
8 changed files with 358 additions and 1 deletions
+5 -1
View File
@@ -1,10 +1,14 @@
//! Errores del orquestador.
use serde::{Deserialize, Serialize};
use ulid::Ulid;
/// Falla de una operación del `Engine`. Las impls concretas mapean sus
/// errores internos (encarnación, IPC, SSH) a estas variantes.
#[derive(Debug, thiserror::Error)]
///
/// Es `Serialize`/`Deserialize` porque viaja por el wire del
/// `DaemonEngine` (postcard sobre Unix socket).
#[derive(Debug, Clone, thiserror::Error, Serialize, Deserialize)]
pub enum EngineError {
/// No existe ninguna entidad activa con ese `card_id`.
#[error("card `{0}` no encontrada")]