feat(agorapura): app demo — escenario narrado del ágora
CLI que recorre el caso canónico de extremo a extremo: Venezuela
atestigua la nacionalidad de Yumaira, otras identidades corroboran,
una firma manipulada se rechaza, y tres políticas negociadas dan
veredictos distintos sobre la misma evidencia. cargo run -p agorapura.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
4e27065a153c988ebbd435dc3cd8ffd1c0e38c4f · por sergio · 2026-05-20T16:39:07Z (hace 5 meses) · padre c1c136954 · ver el árbol en este commit
Delta semántico · 2
crates/apps/agorapura/src/main.rs
diff --git a/Cargo.lock b/Cargo.lock
index cb340e1..69a8e35 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -80,6 +80,14 @@ dependencies = [
"tokio",
]
+[[package]]
+name = "agorapura"
+version = "0.1.0"
+dependencies = [
+ "agorapura-core",
+ "agorapura-graph",
+]
+
[[package]]
name = "agorapura-core"
version = "0.1.0"
diff --git a/Cargo.toml b/Cargo.toml
index 95bf776..f815254 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -235,6 +235,7 @@ members = [
"crates/apps/cosmobiologia-server",
"crates/apps/dominium",
"crates/apps/fana",
+ "crates/apps/agorapura",
]
[workspace.package]
diff --git a/crates/apps/agorapura/Cargo.toml b/crates/apps/agorapura/Cargo.toml
new file mode 100644
index 0000000..59436a9
--- /dev/null
+++ b/crates/apps/agorapura/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "agorapura"
+version.workspace = true
+edition.workspace = true
+rust-version.workspace = true
+license.workspace = true
+authors.workspace = true
+publish.workspace = true
+description = "agorapura — demostración narrada del ágora de identidad: identidades, claims, atestaciones firmadas y evaluación de confianza bajo políticas negociadas."
+
+[[bin]]
+name = "agorapura"
+path = "src/main.rs"
+
+[dependencies]
+agorapura-core = { path = "../../modules/agorapura/agorapura-core" }
+agorapura-graph = { path = "../../modules/agorapura/agorapura-graph" }
diff --git a/crates/apps/agorapura/src/main.rs b/crates/apps/agorapura/src/main.rs
new file mode 100644
index 0000000..b082c35
--- /dev/null
+++ b/crates/apps/agorapura/src/main.rs
@@ -0,0 +1,91 @@
+//! `agorapura` — demostración narrada del ágora de identidad.
+//!
+//! Recorre el escenario canónico de extremo a extremo: la institución
+//! *Venezuela* atestigua la nacionalidad de la persona *Yumaira*, y
+//! otras identidades la corroboran. Imprime la evidencia acumulada y
+//! cómo distintas políticas *negociadas* la aceptan o no.
+//!
+//! No es la app definitiva — es un smoke test legible y la mejor forma
+//! de ver el módulo funcionando: `cargo run -p agorapura`.
+
+use agorapura_core::{Attestation, Claim, IdentityKind, Keypair};
+use agorapura_graph::{TrustGraph, TrustPolicy};
+
+/// Segundo Unix fijo para que la demo sea reproducible.
+const T0: u64 = 1_700_000_000;
+
+fn main() {
+ println!("\n ágora · demostración de identidad federada\n");
+
+ // --- Identidades. Semillas fijas → demo reproducible. ---
+ let yumaira = Keypair::from_seed([20; 32]);
+ let venezuela = Keypair::from_seed([10; 32]);
+ let comunidad = Keypair::from_seed([30; 32]);
+ let vecina = Keypair::from_seed([40; 32]);
+
+ let mut agora = TrustGraph::new();
+ agora.register(yumaira.identity(IdentityKind::Person, "Yumaira"));
+ agora.register(venezuela.identity(IdentityKind::Institution, "Venezuela"));
+ agora.register(comunidad.identity(IdentityKind::Community, "Vecinos del Valle"));
+ agora.register(vecina.identity(IdentityKind::Person, "Carmen"));
+
+ println!(" identidades registradas:");
+ for kp in [&yumaira, &venezuela, &comunidad, &vecina] {
+ let id = kp.identity_id();
+ let name = agora.identity(id).map(|i| i.display_name.as_str()).unwrap_or("?");
+ println!(" {id} {name}");
+ }
+
+ // --- Atestaciones sobre la nacionalidad de Yumaira. ---
+ let nacionalidad = |by: &Keypair| {
+ Attestation::create(
+ by,
+ Claim::new(yumaira.identity_id(), "nacionalidad", "venezolana", T0),
+ )
+ };
+ println!("\n atestaciones de «nacionalidad = venezolana» sobre Yumaira:");
+ for (by, label) in [
+ (&venezuela, "Venezuela (institución)"),
+ (&comunidad, "Vecinos del Valle (comunidad)"),
+ (&yumaira, "Yumaira (ella misma)"),
+ ] {
+ let att = nacionalidad(by);
+ match agora.add_attestation(att) {
+ Ok(()) => println!(" ✔ firma verificada — {label}"),
+ Err(e) => println!(" ✘ rechazada — {label}: {e}"),
+ }
+ }
+
+ // --- Intento de fraude: una firma manipulada. ---
+ let mut falsa = nacionalidad(&vecina);
+ falsa.claim.value = "marciana".into(); // rompe la firma
+ print!("\n intento de atestación con firma manipulada: ");
+ match agora.add_attestation(falsa) {
+ Ok(()) => println!("ACEPTADA (esto sería un bug)"),
+ Err(e) => println!("rechazada — {e}"),
+ }
+
+ // --- Corroboración. ---
+ let c = agora.corroboration(yumaira.identity_id(), "nacionalidad", "venezolana");
+ println!("\n corroboración del claim:");
+ println!(" atestadores totales : {}", c.total());
+ println!(" terceros (no ella) : {}", c.third_party());
+ println!(" auto-atestado : {}", c.self_attested);
+
+ // --- Veredicto según la política negociada. ---
+ println!("\n veredicto según la política (la verdad depende de lo pactado):");
+ for (policy, label) in [
+ (TrustPolicy::strict(1), "laxa · 1 tercero basta"),
+ (TrustPolicy::strict(2), "media · 2 terceros"),
+ (TrustPolicy::strict(3), "estricta · 3 terceros"),
+ ] {
+ let ok = policy.accepts(&c);
+ let mark = if ok { "ACEPTA" } else { "rechaza" };
+ println!(" [{mark}] {label}");
+ }
+
+ println!(
+ "\n el ágora no dicta la verdad: acumula evidencia firmada y\n \
+ cada quien la pesa con la política que negocie.\n"
+ );
+}