🦴 Kernel — Espinha Dorsal
O Kernel é o Bounded Context que implementa o loop GALS (Globally Asynchronous, Locally Synchronous) do nó. Ele é a única peça que conhece rede, threads e tempo — o restante dos BCs (
biology,economy,capiba,learning, etc.) é puro domínio e se comunica com o Kernel via troca de mensagens.
flowchart LR
subgraph Adapters [adapters/ · E/S]
NET[NetworkPort<br/>libp2p]
TIME[TimePort<br/>tokio::time]
RES[ResourcePort<br/>sysinfo]
end
subgraph Ports [ports/ · traits de I/O]
P1[NetworkPort]
P2[TimePort]
P3[ResourcePort]
end
subgraph Types [ports/ · tipos canônicos]
T1[PeerId]
T2[MetabolicMessage]
T3[SystemPressure]
end
subgraph Actor [actor/ · loop GALS]
KA[KernelActor<br/>mpsc receiver]
end
subgraph Domain [domain/ · núcleo puro]
M[Metabolism<br/>peer health +<br/>token bucket]
end
NET -.implementa.-> P1
TIME -.implementa.-> P2
RES -.implementa.-> P3
P1 --> KA
P2 --> KA
P3 --> KA
KA -->|handle_message<br/>carrega T1, T2, T3| M
M -->|MetabolicAction| KA
KA -->|send_message| P1
Regra de dependência:
domain/nunca importa deactor/,ports/ouadapters/. A seta vai doactorpara odomain— nunca o contrário. Esta é a aplicação do Dogma 1 (Isolamento Hexagonal) ao Kernel.
0.1. O coração metabólico: Token Bucket
O Metabolism é o algoritmo central do GALS: um token bucket
canônico com MAX_TOKENS=100 e recarga de 0.1 tokens/ms. Cada
mensagem consome 1 token; sem tokens, o ator não bloqueia —
ele marca a mensagem como saturada, dispara sinal algedônico e
segue. É a implementação concreta do princípio de
Backpressure universal.
flowchart LR
I[Inbound msg] --> R[Refill<br/>tokens = min MAX, t + tokens]
R --> C{tokens >= 1?}
C -- não --> S[Saturated<br/>drop + algedonic]
C -- sim --> H[handle_message]
H --> T[tokens -= 1]
T --> A[MetabolicAction<br/>processa e despacha]
S -.sinal.-> ALG[AlgedonicSensor]
A --> O[Outbound msg]
1. Anatomia do crate
crates/kernel/src/
├── lib.rs # exports públicos
├── actor/
│ └── mod.rs # KernelActor, KernelCommand, KernelHandle
├── domain/
│ └── mod.rs # Metabolism, MetabolicAction, PeerHealth
├── ports/
│ └── mod.rs # PeerId, MetabolicMessage, NetworkPort, TimePort, ResourcePort, SystemPressure
└── adapters/
└── mod.rs # (vazio por enquanto — implementações concretas virão aqui)
| Módulo | Camada hexagonal | Responsabilidade |
|---|---|---|
domain/ | Núcleo | Lógica pura: tracking de peers, backpressure, decisão metabólica |
ports/ | Portas | Traits assíncronos que o domínio “vê” do mundo |
actor/ | Aplicação | Loop GALS que conecta domínio a portas |
adapters/ | Adaptadores | Implementações concretas de portas (rede, tempo, recursos) |
2. Domínio (crates/kernel/src/domain/)
Metabolism
A única estrutura que carrega estado mutável do domínio. Não tem
I/O, não tem threads, não tem tokio. É testável em isolamento
absoluto com um relógio mockado.
#![allow(unused)]
fn main() {
pub struct Metabolism {
node_id: PeerId,
known_peers: HashMap<PeerId, PeerHealth>,
tokens: f32, // token bucket (backpressure)
last_refill_ms: u64,
}
}
Constantes de metabolismo:
| Constante | Valor | Significado |
|---|---|---|
MAX_TOKENS | 100.0 | Capacidade máxima do bucket |
REFILL_RATE_PER_MS | 0.1 | 100 tokens / segundo |
Algoritmo:
- Em cada
handle_message, primeiro reabastece o bucket proporcional anow_ms - last_refill_ms. - Se
tokens < 1.0, retornaMetabolicAction::Saturated(sem consumir). - Senão, consome 1 token e processa a mensagem.
- Para
PulseRequest: registra/atualiza oPeerHealthdo emissor e agendaPulseResponse. - Para
PulseResponse: atualiza ohealth_scoredo emissor.
MetabolicAction
#![allow(unused)]
fn main() {
pub enum MetabolicAction {
SendResponse { target: PeerId, msg: MetabolicMessage },
UpdateInternalState,
Saturated, // backpressure sinalizou overload
None,
}
}
Invariante:
Metabolism::handle_messageé puro — dado o mesmo(sender, msg, now_ms)e o mesmo estado inicial, produz a mesmaMetabolicAction. Isso é verificável porproptest.
PeerHealth
#![allow(unused)]
fn main() {
pub struct PeerHealth {
pub last_seen_ms: u64,
pub health_score: f32,
}
}
health_score ∈ [0.0, 1.0]; valores perto de 0 indicam peer
degradado e disparam o sensor algedônico (futuro: roteamento
adaptativo).
3. Portas (crates/kernel/src/ports/)
Definem tudo que o domínio vê do mundo, sem saber quem implementa.
PeerId
#![allow(unused)]
fn main() {
pub struct PeerId([u8; 32]);
}
32 bytes opacos. Display mostra os primeiros 8 bytes em hex
(para logs).
MetabolicMessage
#![allow(unused)]
fn main() {
pub enum MetabolicMessage {
PulseRequest { timestamp: u64 },
PulseResponse { timestamp: u64, health: f32 },
}
}
Por enquanto, apenas duas variantes — hello da malha. O conjunto
crescerá conforme os outros BCs (biology, economy) enviarem
mensagens de domínio. Cada nova variante é uma mudança compatível
para trás (apenas adição), preservável por versionamento de envelope.
NetworkPort (assíncrono)
#![allow(unused)]
fn main() {
#[async_trait]
pub trait NetworkPort: Send + Sync {
/// Envia uma mensagem metabólica para um peer específico.
async fn send_message(&self, target: PeerId, msg: MetabolicMessage) -> Result<(), &'static str>;
/// Broadcast de uma mensagem para todos os peers conhecidos.
async fn broadcast(&self, msg: MetabolicMessage) -> Result<(), &'static str>;
}
}
Implementação concreta atual: apps/node/src/network.rs (libp2p).
TimePort (síncrono)
#![allow(unused)]
fn main() {
pub trait TimePort: Send + Sync {
fn now_ms(&self) -> u64;
}
}
Síncrono de propósito: facilita proptest (testes determinísticos
bancam o tempo com um contador). Não usar Instant::now()
diretamente no domínio.
ResourcePort (síncrono)
#![allow(unused)]
fn main() {
pub trait ResourcePort: Send + Sync {
fn get_current_pressure(&self) -> SystemPressure;
}
}
SystemPressure é o que alimenta o sensor algedônico do nó
(local, sem supervisor global).
#![allow(unused)]
fn main() {
pub struct SystemPressure {
pub cpu_usage: f32, // [0.0, 1.0]
pub memory_usage: f32, // [0.0, 1.0]
pub disk_usage: f32, // [0.0, 1.0]
}
}
4. Ator (crates/kernel/src/actor/)
KernelCommand
Mensagens que o Node (camada de fora) envia para o ator:
#![allow(unused)]
fn main() {
pub enum KernelCommand {
IncomingMessage { sender: PeerId, message: MetabolicMessage },
TriggerPulse,
Shutdown,
}
}
KernelActor
#![allow(unused)]
fn main() {
pub struct KernelActor {
metabolism: Metabolism,
network_port: Arc<dyn NetworkPort>,
time_port: Arc<dyn TimePort>,
resource_port: Arc<dyn ResourcePort>,
receiver: mpsc::Receiver<KernelCommand>,
}
}
O loop principal:
#![allow(unused)]
fn main() {
pub async fn run(mut self) {
while let Some(command) = self.receiver.recv().await {
match command {
KernelCommand::IncomingMessage { sender, message } => {
let now = self.time_port.now_ms();
let action = self.metabolism.handle_message(sender, message, now);
self.execute_action(action).await;
}
KernelCommand::TriggerPulse => { /* pulso metabólico */ }
KernelCommand::Shutdown => break,
}
}
}
}
Regra do Dogma 2 (GALS): o loop do ator nunca bloqueia em I/O. Toda chamada externa é
asynceawaitada; ompsc::Receiveré o único ponto de suspensão.
KernelHandle
pub use em lib.rs — é a handle que o Node usa para enviar
comandos ao ator:
#![allow(unused)]
fn main() {
pub struct KernelHandle {
sender: mpsc::Sender<KernelCommand>,
}
}
#![allow(unused)]
fn main() {
impl KernelHandle {
pub async fn incoming(&self, sender: PeerId, msg: MetabolicMessage) -> Result<(), ...>;
pub async fn trigger_pulse(&self) -> Result<(), ...>;
pub async fn shutdown(&self) -> Result<(), ...>;
}
}
5. Adaptadores (crates/kernel/src/adapters/)
Por enquanto apenas:
#![allow(unused)]
fn main() {
pub mod networking {}
}
A implementação concreta está em apps/node/src/network.rs — uma
decisão consciente: o Kernel é uma biblioteca no_std-friendly
que não inclui libp2p. A integração com a rede física vive no
Node (binário). Em fases futuras, a tendência é mover
implementações para cá para reuso em outros front-ends (CLI,
playground, simulator).
6. Fluxo end-to-end (exemplo)
Uma mensagem chegando do libp2p:
sequenceDiagram
participant N as Network (libp2p)
participant H as KernelHandle
participant A as KernelActor (loop)
participant D as Metabolism (domain)
participant R as ResourcePort
N->>H: incoming(sender, PulseRequest)
H->>A: mpsc::send(IncomingMessage)
A->>R: get_current_pressure()
A->>D: handle_message(sender, msg, now)
D-->>A: MetabolicAction::SendResponse
A->>N: network_port.send_message(...)
N-->>Sender: PulseResponse
Garantia: todo o caminho passa por um único thread (o loop do ator). Não há
MutexnoMetabolism— a imutabilidade estrutural doactoré o “lock”.
Transporte e descoberta do pulso
A integração libp2p concreta vive em apps/node/src/network.rs. O
pulso trafega em gossipsub (tópico paebiru-metabolism), que é
o canal de publicação efêmero e ponto-a-ponto. Os peers que assinam
este tópico são descobertos por duas camadas complementares:
- mDNS (LAN) — plug-and-play local, sem configuração.
- Kademlia DHT (global) — anel de peers roteáveis, alimentado
por
PAEBIRU_BOOTNODES. O gate F1 do Roadmap (“dois nós trocam pulse ponta-a-ponta via libp2p Kademlia”) é satisfeito pela combinação Kademlia-para-descoberta + gossipsub-para-transporte; o pulso não é armazenado como Kademlia Record — ele é efêmero por design (ver Pulse no Dicionário).
O Dogma 1 (Hexagonal) é preservado: este BC não conhece
libp2p, mDNS ou Kademlia. Toda a complexidade de transporte vive
no Node (apps/node), satisfazendo o NetworkPort que o
KernelActor consome. O Dogma 3 (no_std) também permanece
intacto — o paebiru-kernel continua compilável para
riscv32imc-unknown-none-elf e thumbv7em-none-eabihf.
7. Como testar
| Tipo de teste | Onde | O que cobre |
|---|---|---|
Unitário (#[test]) | crates/kernel/src/domain/tests | Metabolism puro — proptest! sobre (sender, msg, now) |
| Integração | crates/kernel/tests/actor_loop.rs | KernelActor::run com NetworkPort mockado |
| Property-based | crates/kernel/proptest-recipes | Invariante: tokens ∈ [0, MAX_TOKENS] |
| no_std | cargo check --target riscv32imc-unknown-none-elf | domain/ compila sem std |
Cobertura-alvo: ≥ 90 % (enforçada por make coverage).
8. O que não é Kernel
- Persistência →
capiba.md(outro BC). - Aprendizado / modelos → BC
learning. - Crédito / Loteria Joule → BC
economy. - Agentes BDI / SNN → BC
biology. - Definição de plasmídeos → BC
plasmids.
Se a mudança que você quer fazer não é loop de ator, porta ou adaptação de E/S, provavelmente é outro BC.
9. Veja também
- Dicionário · GALS
- Dicionário · Maturidade Causal
- Dicionário · Sensor algedônico
- Capiba · Memória persistente (o BC vizinho que consome receipts do Kernel)
- Quatro Dogmas Inquebráveis
- Glossário da skill
paebiru-architect