# 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](vendor/oasis/src/models/banking_model.js#L35) | `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](vendor/oasis/src/models/banking_model.js#L617) | `(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](vendor/oasis/src/models/stats_model.js#L438) | `` 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](vendor/oasis/src/models/panicmode_model.js#L10) | `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](vendor/oasis/src/backend/opinion_categories.js#L1) | `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](vendor/oasis/src/models/opinions_model.js#L59) | `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](vendor/oasis/src/views/settings_view.js#L24) | `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](vendor/oasis/src/models/inhabitants_model.js#L65-L73) | `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](vendor/oasis/src/models/parliament_model.js#L17-L21) | `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](vendor/oasis/src/models/parliament_model.js#L23) | `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](vendor/oasis/src/models/banking_model.js#L14-L21) | `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](vendor/oasis/src/models/typed_log.js#L77) | `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](vendor/oasis/src/models/banking_model.js#L840) | `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](vendor/oasis/src/models/banking_model.js#L1151) | `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](vendor/oasis/src/server/SSB_server.js#L57) | `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](vendor/oasis/src/server/SSB_server.js#L60-L61) | `.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](vendor/oasis/src/configs/server-config.json#L12) | `"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](vendor/oasis/src/AI/ai_service.mjs#L89) | `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](vendor/oasis/src/AI/ai_service.mjs#L52) | `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](vendor/oasis/src/models/wallet_model.js#L22-L23) | `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](vendor/oasis/src/models/tombstone_validator.js#L12) | `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](vendor/oasis/src/models/parliament_model.js#L1104) | `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](vendor/oasis/src/models/stats_model.js#L24) | `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](vendor/oasis/src/backend/backend.js#L7306) | `.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](vendor/oasis/src/configs/server-config.json#L25) | `"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](vendor/oasis/src/models/tribes_model.js#L11) | `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](vendor/oasis/src/models/logs_model.js#L243) | `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](vendor/oasis/src/models/logs_model.js#L137) | `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](vendor/oasis/src/views/AI_view.js#L61) | `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](vendor/oasis/src/models/larp_model.js#L139) | `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](vendor/oasis/src/models/melody_model.js#L124) | `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](vendor/oasis/src/models/parliament_model.js#L1298) | `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](vendor/oasis/src/models/courts_model.js#L139) | `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](vendor/oasis/src/models/courts_model.js#L142-L150) | `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](vendor/oasis/src/models/industry_model.js#L164) | `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](vendor/oasis/src/models/banking_model.js#L215-L219) | `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](vendor/oasis/src/backend/backend.js#L10273) | `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](vendor/oasis/src/models/crypto.js#L146) | `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](vendor/oasis/src/models/tribes_model.js#L753) | `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](vendor/oasis/src/backend/backend.js#L4651) | `.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](vendor/oasis/scripts/oasis-pub.js#L41) | `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](vendor/oasis/src/models/parliament_model.js#L26-L27) | `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](vendor/oasis/src/models/parliament_model.js#L373) | `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](vendor/oasis/src/models/exportmode_model.js#L24-L26) | `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](vendor/oasis/src/backend/backend.js#L8054-L8057) | `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](vendor/oasis/src/backend/backend.js#L9556) | `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](vendor/oasis/src/models/inhabitants_model.js#L87) | `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](vendor/oasis/src/models/parliament_model.js#L1191) | `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](vendor/oasis/src/views/parliament_view.js#L21) | `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](vendor/oasis/src/models/parliament_model.js#L422-L423) | `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](vendor/oasis/src/models/parliament_model.js#L650) | `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](vendor/oasis/src/models/inhabitants_model.js#L217-L220) | `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](vendor/oasis/src/models/polls_model.js#L99) | `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`): ```json [ {"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).