audit-verify, autopromote, histogramas y hot-reload caps

- audit::verify_chain_from_cas() recorre prev_sha desde head hasta genesis,
  valida sha256(blob)==sha del CAS para detectar tampering. Endpoint
  VerifyAudit + brainctl audit-verify.
- autopromote loop background: cada N segundos detecta cristales sobre
  threshold y los promueve sin operador. Anti-doble vía HashSet de pares
  (ant, con). Flag --autopromote-secs.
- GapHistogram con buckets exponenciales (1ms..1000s) capturado en
  observer.record(). top_gap_pairs(K) limita cardinalidad. Expuesto en
  Prometheus como ente_brain_pair_gap_seconds histogram per pair.
- BusRequest::UpdateCapabilities con auth obligatorio (sólo el dueño puede
  modificar sus caps). Incarnated.dynamic_provides separa runtime de la
  Card immutable. Graph mediator actualiza providers index + revoca grants.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sergio
2026-05-03 23:32:54 +00:00
parent a4fa42c781
commit badf4257ec
12 changed files with 406 additions and 8 deletions
+18 -1
View File
@@ -42,6 +42,7 @@ struct CliArgs {
audit_head: Option<PathBuf>,
metrics_addr: Option<String>,
brain_half_life: Option<f64>,
autopromote_secs: Option<u64>,
}
fn parse_args() -> CliArgs {
@@ -53,6 +54,7 @@ fn parse_args() -> CliArgs {
let mut audit_head = None;
let mut metrics_addr = None;
let mut brain_half_life = None;
let mut autopromote_secs = None;
while let Some(a) = args.next() {
match a.as_str() {
"--checkpoint" => checkpoint = args.next().map(PathBuf::from),
@@ -62,10 +64,14 @@ fn parse_args() -> CliArgs {
"--audit-head" => audit_head = args.next().map(PathBuf::from),
"--metrics-addr" => metrics_addr = args.next(),
"--brain-half-life" => brain_half_life = args.next().and_then(|s| s.parse().ok()),
"--autopromote-secs" => autopromote_secs = args.next().and_then(|s| s.parse().ok()),
other => warn!(arg = %other, "argumento desconocido, ignorado"),
}
}
CliArgs { checkpoint, restore, rules, rules_out, audit_head, metrics_addr, brain_half_life }
CliArgs {
checkpoint, restore, rules, rules_out, audit_head,
metrics_addr, brain_half_life, autopromote_secs,
}
}
fn main() -> anyhow::Result<()> {
@@ -94,6 +100,7 @@ fn main() -> anyhow::Result<()> {
card, dev_mode,
cli.checkpoint, cli.rules, cli.rules_out,
cli.audit_head, cli.metrics_addr, cli.brain_half_life,
cli.autopromote_secs,
))
}
@@ -106,6 +113,7 @@ async fn primordial_loop(
audit_head: Option<PathBuf>,
metrics_addr: Option<String>,
brain_half_life: Option<f64>,
autopromote_secs: Option<u64>,
) -> anyhow::Result<()> {
info!(seed_id = %seed_card.id, label = %seed_card.label, "Ente #0 entra al bucle primordial");
@@ -158,6 +166,15 @@ async fn primordial_loop(
*obs = ente_brain::Observer::new(1024).with_half_life(hl);
info!(hl_secs = hl, "observer con time-decay activo");
}
if let Some(secs) = autopromote_secs {
ente_brain::spawn_autopromote_loop(
brain.clone(),
ente_brain::AutopromoteParams {
interval_secs: secs,
threshold: brain.params, // mismo threshold que crystals manuales
},
);
}
// Si --audit-head, configuramos el head pointer y arrancamos auto-flush.
if let Some(head_path) = audit_head {
// Re-creamos el AuditLog con head pointer.