El modelo de visibilidad de .me
Regla base: todo es público por defecto.
me.wallet(500) // público — cualquiera puede leer
me.name("Abella") // público
me.profile.city("CDMX") // público
El operador _ es el único que cambia eso. Es un declarador de scope secreto — no un flag global, sino una instrucción estructural atada a un path específico.
Cómo funciona _ exactamente
// Sin secret: TODO público
me.wallet(500)
me("wallet") // → 500 ✅ visible
// Con secret: el root se vuelve stealth, las hojas siguen accesibles
me.wallet["_"]("vault-key")
me.wallet.balance(500)
me.wallet.hidden.seed("abc")
me("wallet") // → undefined ← stealth root (A0)
me("wallet.balance") // → 500 ← readable con la clave correcta
me("wallet.hidden.seed") // → "abc" ← readable
El punto clave: me.wallet["_"]("vault-key") no cifra el path wallet como string — cifra el blob del branch wallet usando "vault-key" como semilla de derivación criptográfica. El root devuelve undefined no porque no exista, sino por diseño intencionado (honest absence).
Tu intuición sobre wallet["_"].walletB — así se hace
// wallet es público
me.wallet.balance(1000) // visible a todos
// walletB es secreto, dentro de wallet
me.wallet.walletB["_"]("my-secret-key")
me.wallet.walletB.seed("abc")
me.wallet.walletB.amount(500)
me("wallet.balance") // → 1000 ← público ✅
me("wallet.walletB") // → undefined ← stealth root ✅
me("wallet.walletB.amount") // → 500 ← readable con key ✅
El subespacio walletB está dentro del espacio público wallet, pero tiene su propio scope secreto. Eso es la ley de refinamiento: wallet.walletB ⊆ wallet, y A(wallet.walletB) ⊆ A(wallet).
Nested secrets (A3b del test)
me.root["_"]("alpha") // root secreto
me.root.child["_"]("beta") // child también secreto, dentro de root
me("root") // → undefined (stealth)
me("root.child") // → undefined (stealth)
me("root.child.leaf") // → "x" (el owner puede leer)
me.as(null)("root.child.leaf") // → undefined (guest bloqueado)
El mapa completo de operadores
| Operador | Qué hace | Default sin él |
|---|---|---|
["_"]("key") |
Declara secret scope en ese path | Público |
["~"]("seed") |
Reset de noise — rompe herencia de key | Derivación continua desde padre |
["__"]("target") o ["->"] |
Pointer estructural a otro path | Sin pointer |
["@"]("user") |
Identity claim normalizado | Sin identity |
["?"](...fields) |
Query como evento en memoria | Sin audit |
["-"]("field") |
Tombstone/borrado auditable | Sin borrado |
["="]("expr") |
Derivación/cálculo | Sin cálculo |
Lo que esto significa para NRP
Cuando NRP sirve un path HTTP:
GET /@jabellae/wallet → stealth root → devolver null / origin:"stealth"
GET /@jabellae/wallet/balance → depende de si hay key material
GET /@jabellae/name → público → devolver valor
El servidor NRP nunca debe revelar si undefined es “no existe” o “existe pero es stealth” — ambos casos son indistinguibles para un observador sin la clave. Eso es lo que el Disclosure Model (Section 6 del NRP doc) formaliza, y lo que todavía falta implementar correctamente en el binding HTTP actual.