ente-zero (PID 1 del fractal arje) ahora levanta el server de
brahman-handshake junto al ente-bus existente, escuchando en
\$BRAHMAN_INIT_SOCKET (default \$XDG_RUNTIME_DIR/brahman-init.sock).
Es un canal paralelo dedicado a módulos brahman-conscientes que se
presentan con una Card y declaran flujos tipados.
Cambios:
- crates/core/brahman-handshake/src/transport.rs: helper nuevo con
resolución XDG_RUNTIME_DIR → TMPDIR, override por var de entorno
BRAHMAN_INIT_SOCKET. Test unitario para el override.
- crates/core/brahman-handshake/Cargo.toml: example "probe" + dev-dep
anyhow. Probe sirve como herramienta de diagnóstico para conectar
contra cualquier server vivo.
- crates/core/brahman-handshake/examples/probe.rs: cliente mínimo que
hace Hello → Ping → Farewell e imprime el HelloAck recibido.
- crates/core/ente-zero/Cargo.toml: dependencias brahman-handshake
+ brahman-broker.
- crates/core/ente-zero/src/main.rs: en primordial_loop, tras spawn
del ente-bus, crea Arc<Mutex<Broker>> compartido y llama
Server::bind. Si el bind falla (FS no escribible, socket en uso),
loggea y degrada a "modo bus-only" — la doctrina PID 1 no rompe por
subsistemas opcionales (mismo patrón que uevents).
Validación end-to-end manual:
$ BRAHMAN_INIT_SOCKET=/tmp/e2e.sock ./target/debug/ente-zero &
$ BRAHMAN_INIT_SOCKET=/tmp/e2e.sock cargo run --example probe
HelloAck: session=01KR41Q8... server=0.1.0 protocol=0.1.0 init_attached=true
Pong: ts=1778252489714ms
Farewell OK
Tests: 27/27 (broker 11 + card 8 + handshake codec+transport 2 + integ 6).
cargo check --workspace: 0 errores.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ServerConfig acepta un Option<Arc<Mutex<Broker>>> compartido. Cuando está
presente, el servidor lo mantiene en sincronía con las sesiones:
- Tras un Hello aceptado, register_session indexa la Card en el broker
ANTES de insertar en el SessionRegistry y de emitir HelloAck.
- Al cerrar la sesión (Farewell, EOF, o error en run_post_handshake), un
cleanup() unificado llama unregister en el broker y remove en el
SessionRegistry. Garantizado por refactor de Session::handle a
do_handshake → run_post_handshake → cleanup.
Tests nuevos en handshake.rs:
- broker_registers_and_unregisters_with_session: confirma el ciclo
register → farewell → unregister.
- broker_matches_two_live_modules: dos clientes (productor + consumidor)
conectados; el broker resuelve find_producer_for(consumer.session, "in")
→ producer "dht". Tras farewell del productor, el match desaparece.
Fix colateral: brahman-card::TypeRef pasa de internally-tagged
(#[serde(tag = "kind")]) a externally-tagged (default). Postcard no
soporta internally-tagged en formatos no self-describing — sin este
cambio el wire de Hello con Cards que tengan flujos no codificaba.
JSON cambia de {"kind":"primitive","name":"x"} a
{"primitive":{"name":"x"}}. Documentado en el doc-comment de TypeRef.
26/26 tests verdes (broker 11 + card 8 + handshake codec 1 + integ 6).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Crate nuevo en crates/core/brahman-broker que indexa Cards por SessionId
y empareja flow.input de un consumidor con flow.output del productor más
adecuado.
Tres ejes de matching:
1. Estrategia (MatchStrategy):
- Exact: igualdad estricta de TypeRef.
- Structural: misma forma — para Wit, mismo package + name (ignora
interface); para Primitive, mismo name.
- ExactThenStructural (default): prefiere Exact; cae en Structural si
no hay. Reporta cuál ganó en Match.via.
2. Override pin_to: si el consumidor declara pin_to = "label", el broker
prefiere productores con ese label (siempre que el tipo matchee).
Si la pista falla, cae en type-search general. Match.pinned indica
qué camino se siguió.
3. Prioridad: empate de tipo se resuelve por Card.priority (Critical >
High > Normal > Low). Empate de prioridad se resuelve lexicográfica-
mente por label (estable y determinista).
API mínima:
- Broker::new(config)
- register(session, &Card) / unregister(session)
- find_producer_for(consumer_session, input_name) -> Option<Match>
- all_matches() -> Vec<Match> (introspección)
El broker es stateless w.r.t. routes: cada query se computa bajo demanda.
Sólo persiste el índice de BrokeredCard (vista mínima: label, priority,
inputs, outputs).
Cambio aditivo en brahman-card: Priority deriva PartialOrd/Ord/Hash para
ser usable como tiebreaker.
Tests: 11/11.
- exact_match_same_typeref
- structural_ignores_interface
- exact_strategy_rejects_interface_mismatch
- exact_then_structural_prefers_exact
- pin_to_overrides_type_search
- pin_to_unresolvable_falls_back_to_type_match
- priority_breaks_ties
- label_alpha_breaks_priority_ties
- unregister_removes_producer
- no_self_loops
- all_matches_lists_pairs
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Crate nuevo en crates/core/brahman-handshake que implementa el handshake
real del shared_wit/protocol.wit como wire format Rust↔Rust sobre Unix
socket con frames length-prefixed + cuerpo postcard.
Componentes:
- src/messages.rs: Hello, HelloAck, Ping, Pong, Farewell, HandshakeError
y Frame (enum-suma). HandshakeError ya implementa thiserror::Error y
cruza el wire.
- src/codec.rs: write_frame / read_frame asíncronos con MAX_FRAME_BYTES
de 4 MiB. Test interno de roundtrip.
- src/server.rs: Server::bind crea el listener en Unix socket; emite
ResolvedCard tras validar la Card y devuelve ULID como SessionId.
ServerConfig.init_attached se reporta en HelloAck.
- src/client.rs: Client::connect hace pre-validación local de la Card
(fail fast), envía Hello, parsea HelloAck. ping() y farewell() expuestos.
- tests/handshake.rs: 4 tests de integración:
* full_handshake_roundtrip — happy path con 3 pings + farewell
* rejects_invalid_card_client_side — label vacío rechazado pre-envío
* server_rejects_protocol_mismatch — protocol_version 999.0.0 → Error
* ping_before_hello_rejected — Ping sin Hello previo → Rejected
Limitación conocida: postcard no serializa serde_json::Value (variantes
Array/Object con length dinámico). Se removieron por eso los campos
`extensions` (Card) y `extra` (Permissions). Forward-compat queda
cubierta por schema_version + protocol_version negotiation; si más
adelante necesitamos preservar campos JSON desconocidos, irá en un
WireCard separado o un envelope.
13/13 tests verdes (brahman-card 8 + brahman-handshake codec 1 + integ 4).
cargo check --workspace: 0 errores.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ente-card pasa a ser un crate-shim que re-exporta los tipos de
brahman-card bajo sus nombres legacy:
- EntityCard ≡ brahman_card::Card (alias)
- Capability, Payload, SomaSpec, Supervision, etc. — pub use directo
Cambios concretos:
- crates/core/brahman-card/src/lib.rs: añade impl Default for Card.
Permite usar `..Default::default()` en struct-literals para los
campos aditivos (permissions, lifecycle, priority, flow, extensions).
- crates/core/ente-card/src/lib.rs: reescrito como shim de re-export
(~25 líneas). Las definiciones, validaciones y tests viven en
brahman-card.
- crates/core/ente-card/Cargo.toml: deps reducidas a brahman-card; se
eliminan serde/serde_json/ulid (vienen transitivos vía re-export).
- crates/core/ente-zero/src/seed.rs: 4 struct-literals de EntityCard
ahora terminan con `..Default::default()` para cubrir los nuevos
campos del schema híbrido.
Los 21 consumidores de ente-card (ente-zero, ente-bus, ente-brain,
ente-soma, ente-cas, los 12 *-compat, etc.) compilan sin cambios —
sus `use ente_card::EntityCard` y demás imports siguen resolviendo,
ahora a tipos de brahman-card.
cargo test -p brahman-card: 8/8.
cargo build -p ente-zero: OK.
cargo check --workspace: 0 errores.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Crate nuevo en crates/core/brahman-card que unifica el modelo de arje y
el de brahman/core_protocol original:
De arje (ente-card):
- Identidad ULID + lineage opcional
- Capability tipado (Spawn, Journal, Endpoint, Device, Netlink, ...)
- Payload discriminado (Wasm | Native | Virtual | Legacy)
- SomaSpec (namespaces, cgroups, rlimits, cpu_affinity)
- Supervision (Restart con backoff, OneShot, Delegate)
- genesis: Vec<Card> recursivo
- Validación exhaustiva (label, self-dep, payload, rlimits, cgroup)
Aditivo brahman:
- flow: Flows con TypeRef discriminado (Primitive | Wit)
- pin_to opcional como pista para el broker
- Permissions enumerados (NetworkingPolicy, FsPolicy, IpcPolicy)
- Lifecycle ortogonal (Daemon | Oneshot | Widget)
- Priority de scheduling
- TrustLevel derivado de Permissions (no declarado)
- ResolvedCard con WitInterface opcional
- extensions: HashMap para forward-compat
Formatos: JSON canónico + TOML, auto-detectados por extensión de archivo.
Tests: 8/8 incluyendo arje_seed_format_compatible que valida que el
formato JSON existente de arje sigue parseando con defaults para los
campos nuevos.
ente-card original sigue intacto; los consumidores (ente-zero, etc.)
migrarán en una pasada posterior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>