Costuras de Oasis para el nodo clase (notas de exploración)
Costuras de Oasis 1.0.7 para el nodo clase (materialismo filosófico de Gustavo Bueno)
Exploración de 2026-09-09 sobre vendor/oasis/src @ 9a657b776fcafc7c24bf3ad61825316385ecf513. Cada fila se ha verificado con sed -n y la línea citada contiene el símbolo nombrado. Las categorías siguen las coordenadas K1-K7 del plan embebido en drafts/draft.md. La columna «Lectura» es trituración nuestra: donde interpreta doctrina de Bueno sin verbatim lleva grado de certeza (A/B/C) y la etiqueta [doctrina, sin verbatim]. Los hechos de res_publica/drafts/draftv0.md y otro nodo del catálogo/drafts/draftv0.md se han usado solo como pistas y se han vuelto a verificar aquí.
Rutas: models/ y views/ son relativas a vendor/oasis/src/; backend/backend.js, server/SSB_server.js, AI/ai_service.mjs, configs/server-config.json y ../scripts/oasis-pub.js se citan con su ruta completa desde src/.
Tabla de costuras (54)
| # | Categoría | Fichero:línea | Línea literal | Lectura |
|---|---|---|---|---|
| 1 | K1 · M1 físico | banking_model.js:35 | const carbonGramsFromBytes = (b) => (Number(b) || 0) / ONE_MIB * 0.095; |
La única traducción de M1 (bytes) a mundo físico (gramos CO₂) es una constante sin procedencia. Eutaxia ecológica con número mágico |
| 2 | K1 · M1 físico | banking_model.js:617 | (m) => { try { bytes += Buffer.byteLength(JSON.stringify(m && m.value), 'utf8'); } catch (_) {} }, |
El cuerpo físico del inhabitant es la suma de bytes de su feed (getCarbonGramsForUser, :609). M1 = lo que pesa el log |
| 3 | K1 · M1 físico | stats_model.js:438 | const folderSize = getFolderSize(`${os.homedir()}/.ssb`); |
El disco de ~/.ssb es la única medida material del nodo; se mide almacenamiento, no duración |
| 4 | K1 · M1 físico | panicmode_model.js:10 | await fs.promises.rm(ssbPath, { recursive: true, force: true }); |
Destrucción de M1 (directorio) = destrucción de la identidad (M3, la clave). Los géneros se involucran en el borrado |
| 5 | K1 · M2 fenomenológico | opinion_categories.js:1 | const positive = [ |
La vivencia se tipifica en listas cerradas (positive, constructive). M2 entra en la red solo como categoría |
| 6 | K1 · M2 fenomenológico | opinions_model.js:59 | if (!categories.includes(category)) throw new Error("Invalid voting category."); |
Una opinión fuera del catálogo lanza error: M2 no tiene entrada libre, se reduce a M3 (tipo de mensaje) |
| 7 | K1 · M2 fenomenológico | settings_view.js:24 | const theme = currentThemeConfig.themes?.current || "Dark-SNH"; |
La apariencia (tema) vive en oasis-config.json del nodo, no en el feed: la experiencia es local y no replicada |
| 8 | K1 · M2 fenomenológico | inhabitants_model.js:65 | function bucketLastActivity(ts) { |
La presencia de un sujeto se reduce a tres cubos (green <2w, orange 2w-6m, red ≥6m). Única medida de recurrencia social |
| 9 | K1 · M3 abstracto | parliament_model.js:17 | const TERM_DAYS = 60; |
Las reglas constitutivas (60 días de mandato, 7 de propuesta, 15 de revocación, quórum 2 / 25 %) son constantes del código, sin sujeto que las haya votado |
| 10 | K1 · M3 abstracto | parliament_model.js:23 | const METHODS = ['DEMOCRACY', 'MAJORITY', 'MINORITY', 'DICTATORSHIP', 'KARMATOCRACY']; |
El catálogo de formas de gobierno es M3 cerrado: cinco métodos, no ampliables por el log |
| 11 | K1 · M3 abstracto | banking_model.js:14 | const DEFAULT_RULES = { |
Parámetros de la RBU (alpha, reserveMin, capPerEpoch, caps) fijados en código: capa basal con constantes sin procedencia |
| 12 | K1 · M3 abstracto | typed_log.js:77 | const readTyped = async (ssbClient, types, opts = {}) => { |
Todo el mundo institucional de Oasis es un log leído por type: M3 = tipos de mensaje indexados en cliente |
| 13 | K1 · reducción M3→M1 | banking_model.js:840 | const karmaScore = Math.max(0, Math.round(rawKarma - carbonGrams)); |
Peso político (karma, M3) menos gramos de CO₂ (M1) en una misma resta. Única reducción entre géneros que la red ejecuta; contra la inconmensurabilidad de K1 (A) |
| 14 | K1 · reducción M3→M1 | banking_model.js:1151 | const engagementScore = Math.max(0, Math.round(rawScore - carbonGramsForScore)); |
La misma resta se repite para el engagementScore de la RBU: el fisicalismo alcanza también la redistribución |
| 15 | K2 · cierre criptográfico | SSB_server.js:57 | const Server = SecretStack({ caps }) |
La red se cierra con caps (secret handshake) importado: ciencia ajena, cerrada, parametrizada por el operador (server-config.json:6) |
| 16 | K2 · cierre de replicación | SSB_server.js:60 | .use(require('ssb-ebt')) |
Replicación EBT + ssb-friends (:61): cierre determinista importado; Oasis no lo reabre |
| 17 | K2 · cierre de replicación | server-config.json:12 | "hops": 2 |
El horizonte de replicación (cuántos saltos del grafo de follows se replican) es un parámetro de fichero, no una decisión del cuerpo político |
| 18 | K2 · IA abierta | ai_service.mjs:89 | app.listen(4001); |
El servicio de IA escucha en 4001 sin autenticación: técnica abierta, sin cierre |
| 19 | K2 · IA abierta | ai_service.mjs:52 | const sanitize = (s) => String(s || '').replace(/[<>"']/g, '').replace(/\b(ignore|disregard|forget|system|instruction|prompt)\b/gi, '[$1]').trim();` |
La defensa contra inyección es una regex de siete palabras: no hay cierre operatorio de la categoría «IA» |
| 20 | K2 · banca dependiente | wallet_model.js:22 | const clientrpc = await makeClient(url, user, pass); |
Toda la economía pasa por RPC a una wallet externa (@open-rpc/client-js, :5): categoría dependiente, sin cierre propio |
| 21 | K2 · gobernanza abierta en la ejecución | tombstone_validator.js:12 | if (typeof c === 'object' && c.type === 'tombstone' && typeof c.target === 'string') { |
Derogar es publicar un tombstone que cada cliente valida por su cuenta (:26 exige mismo autor). Cierre en el log, no en la ejecución |
| 22 | K2 · gobernanza abierta en la ejecución | parliament_model.js:1104 | async function enactApprovedChanges(expiringTerm) { |
«Enact» publica mensajes de ley al expirar el mandato; nada fuera de la interfaz obedece. Rama ejecutiva ausente (K5) |
| 23 | K3 · eje radial | stats_model.js:24 | const listPubsFromEbt = () => { |
El mundo objetivo del nodo son pubs y pares leídos de ~/.ssb/ebt: eje radial = infraestructura, no naturaleza |
| 24 | K3 · eje circular | backend.js:7306 | .post("/block/:feed", koaBody(), async (ctx) => { |
La frontera del círculo humano se traza por friend.block (:7307), decisión individual sin órgano |
| 25 | K3 · eje circular | server-config.json:25 | "autofollow": { |
El círculo inicial viene de fábrica: feeds seguidos automáticamente por configuración del proyecto upstream |
| 26 | K3 · eje circular | tribes_model.js:11 | const STRUCTURAL_FIELDS = ['title', 'description', 'image', 'location', 'tags', 'isAnonymous', 'inviteMode', 'status', 'parentTribeId', 'mapUrl']; |
parentTribeId anida círculos: federación de tribus sin órgano federativo (K5 cortical) |
| 27 | K3 · eje angular | logs_model.js:243 | const MAX_ACTIONS = 40; |
La IA narra hasta 40 acciones por clic y las publica como log (:263): sujeto no humano que escribe en el feed. Angular→IA es extensión nuestra (C) [doctrina, sin verbatim] |
| 28 | K3 · eje angular | logs_model.js:137 | const res = await axios.post('http://localhost:4001/ai', { input: prompt, raw: true }, { timeout: 90000 }); |
El númen habita en localhost:4001; la relación con él es HTTP sin auth (ver #18) |
| 29 | K3 · eje angular | AI_view.js:61 | form({ method: 'POST', action: '/ai/approve', class: 'ai-approve-form' }, |
El humano «aprueba» lo que dice la IA antes de publicarlo: único gobierno del eje angular, individual y sin registro |
| 30 | K3 · eje angular | larp_model.js:139 | const SOLAR_AGE_OFFSET = 10000000 - 2026; |
Calendario mítico (año 10.000.000) con nueve casas (HOUSE_KEYS, :9) rotando por mes (:149): estructura numinosa embebida en el código |
| 31 | K3 · eje angular | melody_model.js:124 | function embedTextInWav(wavBuffer, text) { |
Esteganografía LSB en WAV (:140-141): mensajes ocultos para quien sepa leerlos. Comunicación fuera del espacio circular visible |
| 32 | K4/K5 · conjuntiva legislativa | parliament_model.js:1298 | async function canPropose() { |
Quién puede proponer ley depende del mandato vigente (getCurrentTermBase): rama legislativa implementada |
| 33 | K4/K5 · conjuntiva judicial | courts_model.js:139 | const ALLOWED = new Set(['JUDGE', 'DICTATOR', 'POPULAR', 'MEDIATION', 'KARMATOCRACY']); |
Cinco formas de resolver un caso; el veredicto JUDGE solo vale si coincide con judgeId (:363). Rama judicial implementada |
| 34 | K4/K5 · fusión judicial-legislativa | courts_model.js:142 | if (m === 'DICTATOR') { |
El método DICTATOR exige gov.method === 'DICTATORSHIP' (:150) y toma dictatorId del powerId del gobierno (:151): la rama judicial se funde con la conjuntiva bajo dictadura |
| 35 | K4/K5 · basal planificadora | industry_model.js:164 | const steward = rootNode.author |
La instalación industrial tiene un steward único = autor del mensaje raíz; toda mutación exige steward !== ssbClient.id → Unauthorized (:457, :484). Poder basal personal |
| 36 | K4/K5 · basal redistribuidora | banking_model.js:215 | function isPubNode() { |
El nodo que redistribuye es el que se autodeclara en walletPub.pubId === keys.id (:216-218): no hay rama, hay un fichero de config |
| 37 | K4/K5 · basal redistribuidora | backend.js:10273 | pubEngineTimer = setInterval(runPubEngineTick, 30 * 60 * 1000); |
executeEpoch + processPendingClaims cada 30 min sin humano (:10265-10269): redistribución automática. Eutaxia o automatismo, a decidir en D02 |
| 38 | K4/K5 · cortical militar (técnica) | crypto.js:146 | const cipher = crypto.createCipheriv('aes-256-gcm', key, iv); |
Cifrado de tribus AES-256-GCM y reparto de clave con ssbKeys.box (:320): defensa como técnica, no como poder político |
| 39 | K4/K5 · cortical federativa / eutaxia | tribes_model.js:753 | async rotateTribeKey(tribeId, remainingMembers) { |
Al expulsar se genera clave nueva y se redistribuye (:757-760): frontera de pertenencia que se repara sola. Indicador de eutaxia (B) [doctrina, sin verbatim] |
| 40 | K4/K5 · cortical diplomática | backend.js:4651 | .post("/settings/fediverse", koaBody(), async (ctx) => { |
Conectar Mastodon no exige isLoopbackRequest (compárese /update, :9553); la cuenta se guarda en configs/fediverse-accounts.json (fediverse_model.js:5). Diplomacia sin órgano, en manos de cualquier petición |
| 41 | K4/K5 · cortical federativa | oasis-pub.js:41 | const code = await call(ssb.invite.create.bind(ssb.invite), uses); |
Los invites del PUB se crean desde shell (uses por argumento, :40); y el plugin de invite depende de config.pub (SSB_server.js:70). Federación sin rama |
| 42 | K6 · eutaxia (recurrencia) | parliament_model.js:26 | const TERM_EPOCH = Date.UTC(2020, 0, 1); |
Calendario fijo desde 2020-01-01 en ventanas de TERM_SPAN_MS: el ciclo se recurre sin convocante. Eutaxia como recurrencia (B) [doctrina, sin verbatim] |
| 43 | K6 · eutaxia (retorno a anarquía) | parliament_model.js:373 | function virtualAnarchyTerm(window = termWindowFor(Date.now())) { |
Si no hay mandato, el ciclo es ANARCHY virtual con powerType: 'none' (:380): la sociedad política persiste sin gobierno, por defecto de código |
| 44 | K6 · distaxia (identidad) | exportmode_model.js:24 | if (file === 'secret') { |
El export excluye secret: no hay camino de respaldo de la identidad. STEPS del onboarding incluye backup (onboarding_model.js:5) pero solo escribe un flag de fichero (writeFileSync, :116; markStep, :123-125) |
| 45 | K6 · distaxia (irreversibilidad) | backend.js:8054 | await panicmodeModel.removeSSB(); |
Tras borrar, pkill del servidor y process.exit (:8056-8057) sin confirmación en dos pasos. Muerte voluntaria sin fricción |
| 46 | K6 · distaxia (actualización) | backend.js:9556 | const { stdout, stderr } = await exec("git reset --hard && git pull", { cwd: repoRoot }); |
POST /update hace git reset --hard sobre el repo, que contiene src/configs/*.json (caps, hops, wallet): la actualización destruye la configuración local |
| 47 | K6 · sin medida de tiempo | stats_model.js (grep -i uptime: 0 resultados) | — | El nodo mide disco (#3) y pubs (#23) pero no su propia duración: la red no se mide en el tiempo, que es el eje de la eutaxia (B) [doctrina, sin verbatim] |
| 48 | K7 · holización por el log | inhabitants_model.js:87 | const authorsMsgs = (await readTyped(ssbClient, [], { limit: logLimit, withWindow: true })).filter(msg => !!msg.value?.author && … |
El censo = conjunto de autores con mensajes no tombstone: un feed, un inhabitant. Holización mínima: demos definido por haber publicado |
| 49 | K7 · holización por el log | parliament_model.js:1191 | const population = await inhabitantsCount(); |
La población que fija el quórum del mandato sale de ese mismo censo (inhabitantsCount, :244): partes homogéneas contadas |
| 50 | K7 · holización (igualdad formal) | parliament_view.js:21 | if (m === 'DEMOCRACY' || m === 'ANARCHY') return Math.floor(Number(total || 0) / 2) + 1; |
Mayoría = mitad más uno de votos iguales: la forma democrática como aritmética de partes iguales (K7, B) |
| 51 | K7 · des-holización por karma | parliament_model.js:422 | const maxKarma = Math.max(...tied.map(c => c.karma)); |
Empate electoral resuelto por karma: reintroduce partes heterogéneas (notoriedad) en el demos homogéneo |
| 52 | K7 · des-holización por karma | parliament_model.js:650 | if (method === 'DICTATORSHIP' || method === 'KARMATOCRACY') { |
Bajo KARMATOCRACY la revocación sigue otro camino: el karma crea un régimen propio, con propuestas ordenadas por karma (:576) |
| 53 | K7 · des-holización por karma | inhabitants_model.js:217 | const karmaBonus = Math.min(20, Math.log10(1 + Math.max(0, karmaScore)) * 5); |
Sugerencias de contacto ponderan follow (20), karma (≤20), skills (4/u) y actividad (5/2/0): la red re-estratifica el círculo |
| 54 | K7 · un sujeto, un voto | polls_model.js:99 | if (!entry.voters.includes(v.author)) { |
Las opiniones se deduplican por autor de feed: la unidad de cuenta es la clave, no la persona. Holización técnica, no jurídica |
Desajustes de numeración detectados en drafts/draft.md (2026-09-09)
Ambos se han comprobado con sed -n; las líneas del draft apuntan a código cercano pero no al símbolo nombrado.
| Dónde (draft.md) | Dice | Verificado | Nota |
|---|---|---|---|
| §4.1, fila Planificadora (L315) | `steward` único (:163, :462) |
industry_model.js:164 (const steward = rootNode.author) y :457 (if (g.facility.steward !== ssbClient.id) throw new Error("Unauthorized")) |
:163 es if (!rootNode) return null; :462 es name: patch.name === undefined … |
| §4.1, fila Militar (L318) | AES-GCM :146, box :319 |
crypto.js:146 correcto; ssbKeys.box está en :320 (:319 es la cabecera const boxKeyForMember = …) |
Ajuste de una línea |
Operaciones propuestas (para el integrador; el draft semilla no se reescribe salvo nota al margen, así que se dejan como candidatas a draftv0):
[
{"file": "drafts/draft.md", "old": "`steward` único (:163, :462)", "new": "`steward` único (:164, :457)", "motivo": "industry_model.js:164 es `const steward = rootNode.author`; :457 es el `Unauthorized` del steward. Verificado 2026-09-09."},
{"file": "drafts/draft.md", "old": "AES-GCM :146, box :319", "new": "AES-GCM :146, box :320", "motivo": "crypto.js:320 es `ssbKeys.box(tribeKeyHex, [memberFeedId])`; :319 es la cabecera de `boxKeyForMember`. Verificado 2026-09-09."}
]
Ambos old aparecen exactamente una vez (grep -c = 1). Nota: los old tienen menos de 40 caracteres porque la celda es corta; si el integrador exige el mínimo, ampliar a la fila completa de la tabla (L315 y L318, únicas).
Resumen por coordenada
- K1 (materialidades): M1 = bytes/disco/CO₂ (#1-4); M2 = opiniones tipificadas, tema local, cubos de actividad (#5-8); M3 = constantes y tipos de mensaje (#9-12). La red ejecuta una sola reducción entre géneros, karma − gramos (#13-14).
- K2 (cierres): criptografía y replicación cerradas por importación (#15-17); IA abierta (#18-19); banca dependiente (#20); gobernanza cerrada en el log, abierta en la ejecución (#21-22).
- K3 (espacio antropológico): radial = pubs y disco (#23); circular = block, autofollow, tribus anidadas (#24-26); angular = IA 42, L.A.R.P., esteganografía (#27-31), gobernado solo por el botón
/ai/approve. - K4/K5 (capas y ramas): conjuntiva implementada y fusionable bajo dictadura (#32-34); basal en manos de
stewardúnico y nodo autodeclarado pub con motor de 30 min (#35-37); cortical existe como técnica o como shell/config sin órgano (#38-41). - K6 (eutaxia): recurrencia por calendario y anarquía virtual (#42-43); distaxias de identidad, pánico y actualización (#44-46); ausencia de medida de tiempo (#47).
- K7 (holización): el log holiza por autoría (#48-50); el karma des-holiza en empates, régimen propio y sugerencias (#51-53); la unidad de cuenta es la clave (#54).