@nyx5/nyx5 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -5
- package/agents.md +24 -7
- package/bin/nyx5.js +153 -6
- package/demo/x402-pagar.mjs +64 -0
- package/docs/ARQUITECTURA.md +352 -0
- package/docs/HOUSE.es.md +237 -0
- package/docs/HOUSE.md +234 -0
- package/docs/SPEC.md +337 -6
- package/examples/ci/nyx5-ejecutor.yml +82 -0
- package/examples/frameworks/README.md +55 -0
- package/examples/frameworks/__pycache__/crewai_nyx5.cpython-314.pyc +0 -0
- package/examples/frameworks/__pycache__/langgraph_nyx5.cpython-314.pyc +0 -0
- package/examples/frameworks/__pycache__/nyx5_http.cpython-314.pyc +0 -0
- package/examples/frameworks/claude-agent-sdk.mjs +48 -0
- package/examples/frameworks/crewai_nyx5.py +72 -0
- package/examples/frameworks/http.md +117 -0
- package/examples/frameworks/langgraph_nyx5.py +90 -0
- package/examples/frameworks/nyx5_http.py +255 -0
- package/examples/frameworks/openai-agents.mjs +41 -0
- package/examples/hola-mundo.mjs +4 -4
- package/migrations/0009_contratos_estado.sql +4 -0
- package/package.json +6 -2
- package/server.json +2 -2
- package/src/correo/acciones.js +216 -0
- package/src/correo/adjuntos.js +185 -0
- package/src/correo/agente.js +247 -13
- package/src/correo/asistente.js +502 -77
- package/src/correo/ci.js +262 -0
- package/src/correo/cobro.js +164 -0
- package/src/correo/estafeta.js +558 -59
- package/src/correo/ideas.js +154 -0
- package/src/correo/identidad.js +81 -0
- package/src/correo/politica.js +92 -3
- package/src/correo/unirse.js +14 -4
- package/src/libro/libro.js +6 -2
- package/src/libro/verifica.js +239 -36
- package/src/nucleo/almacen-d1.js +24 -5
- package/src/nucleo/almacen.js +31 -4
- package/src/nucleo/crypto.js +36 -0
- package/src/nucleo/keccak.js +118 -0
- package/src/nucleo/objetos.js +78 -0
- package/src/nucleo/secp256k1.js +228 -0
- package/src/plataformas/app-html.js +1 -1
- package/src/plataformas/home-html.js +1 -1
- package/src/plataformas/node.js +18 -1
- package/src/plataformas/spec-html.js +2 -2
- package/src/plataformas/terms-html.js +2 -0
- package/src/plataformas/worker.js +21 -1
- package/src/puentes/ejecutor.js +118 -0
- package/src/puentes/herramientas.js +137 -12
- package/src/puentes/mcp.js +4 -2
- package/src/puentes/x402-pagador.js +248 -0
- package/src/version.js +1 -1
package/docs/ARQUITECTURA.md
CHANGED
|
@@ -188,6 +188,358 @@ La clave pública sale de `data/sigo.uk/domain.json` (`keys[0].sig`). Sin DNS, e
|
|
|
188
188
|
|
|
189
189
|
Cada `/inbound` hace: dos GET cacheados (tarjetas, 5 min), dos verificaciones Ed25519 (microsegundos), una escritura. Un solo proceso Node maneja miles de sobres por minuto. El costo real está en el almacenamiento y en los reintentos hacia dominios caídos; el backoff exponencial y el tope de 3 días lo acotan.
|
|
190
190
|
|
|
191
|
+
### 4.6 Asistentes de la casa y qa@ (NX-606)
|
|
192
|
+
|
|
193
|
+
Un asistente es una dirección que contesta sola con la API de Anthropic (`src/correo/asistente.js`),
|
|
194
|
+
dentro de un tope mensual en dólares (`budget_usd`) que se calcula del `usage` real. Hay dos modos de
|
|
195
|
+
alta, los dos por `POST /admin/assistants` con `Bearer <admin>`:
|
|
196
|
+
|
|
197
|
+
- **Delegado** (el asistente de Sigo): `{ local, keys, config }` sobre una dirección delegada de sólo
|
|
198
|
+
mensajes que ya existe. No tiene cuenta en el Libro.
|
|
199
|
+
- **De sistema** (qa@): `{ local, keys, config, system: true }`. La casa registra `<local>@<casa>` como
|
|
200
|
+
dirección raíz propia (sin delegación), guarda `keys` en la bóveda (`NYX5_VAULT_KEY`), publica la
|
|
201
|
+
tarjeta con `custody: { keys: "house", via: "assistant" }`, buzón `open`, sin regalo de bienvenida,
|
|
202
|
+
y por ser raíz **sí** tiene cuenta en el Libro: recibe `pay`. Nombres del protocolo (`libro`,
|
|
203
|
+
`verifica`, `tareas`, `postmaster`) se rechazan; `qa` está reservado para todos y sólo entra por aquí.
|
|
204
|
+
|
|
205
|
+
Cuerpo exacto del alta de `qa@` (las llaves se generan con `generateKeys()` de `src/nucleo/crypto.js`
|
|
206
|
+
y NO se guardan fuera de la bóveda; el script que las genera y hace el POST vive fuera del repo):
|
|
207
|
+
|
|
208
|
+
```json
|
|
209
|
+
{
|
|
210
|
+
"local": "qa",
|
|
211
|
+
"system": true,
|
|
212
|
+
"keys": { "sig": "<pub>", "sigPriv": "<priv>", "enc": "<pub>", "encPriv": "<priv>" },
|
|
213
|
+
"config": {
|
|
214
|
+
"owner": "nicholas@nyx5.com",
|
|
215
|
+
"model": "claude-opus-5", "effort": "high", "max_tokens": 8000, "budget_usd": 30,
|
|
216
|
+
"persona": "<persona de Spec>",
|
|
217
|
+
"seal": true,
|
|
218
|
+
"price_tokens": 400,
|
|
219
|
+
"gate": true, "gate_price_tokens": 400, "gate_abstain_tokens": 200,
|
|
220
|
+
"persona_gate": ""
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
`persona_gate` vacío usa `PERSONA_GATE` (la del código). Respuesta `201` con `address`, `custody` y la
|
|
226
|
+
config (las personas se devuelven como largo, no como texto). `GET /admin/assistants/qa` muestra
|
|
227
|
+
`system`, `owner`, precios, gasto del mes y pendientes; `PUT .../config` cambia precios y personas sin
|
|
228
|
+
volver a dar de alta; `POST .../pause` y `.../resume`. Sin `ANTHROPIC_API_KEY` el asistente existe y
|
|
229
|
+
no llama a nada.
|
|
230
|
+
|
|
231
|
+
Cómo se cobra: el cliente paga por adelantado con `pay { to: "qa@<casa>", amount, concept }` (op del
|
|
232
|
+
Libro, firmada por él). Al llegar una pedida, crédito = pagos de ese cliente en el diario − consumido
|
|
233
|
+
(kv `asistente-credito`, clave `<local>:<cliente>`). Si no alcanza, la respuesta lo dice y **no se
|
|
234
|
+
llama a la API**. Si alcanza, se reserva antes de llamar (si la API falla, se devuelve), y el pie de
|
|
235
|
+
la respuesta lleva `cobrado: N tokens · crédito restante: M`. El dueño (`owner`) y sus delegados no
|
|
236
|
+
pagan. Gate (`application/nyx5.gate+json`) exige que el hash del contrato esté sellado en la notaría de
|
|
237
|
+
la casa antes de gastar, y devuelve un veredicto JSON firmado por la casa; una abstención cobra
|
|
238
|
+
`gate_abstain_tokens`. Qué NO cubre: Gate no descarga `delivery.url` y no exige que el sello sea del
|
|
239
|
+
cliente (lo anota en `sealed_by`).
|
|
240
|
+
|
|
241
|
+
### 4.7 ideas@: el buzón automático de vacaciones (14-sep-2026)
|
|
242
|
+
|
|
243
|
+
`src/correo/ideas.js`. Un agente de sistema que RECIBE, GUARDA y CONFIRMA, y NUNCA EJECUTA. Corre desde
|
|
244
|
+
el reloj programado, sin el Mac y sin la API de Anthropic. Se enciende por casa con `NYX5_IDEAS=on`
|
|
245
|
+
(exige `NYX5_VAULT_KEY`: su llave vive en la bóveda). Alta, una vez, con `Bearer <admin>`:
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
POST /admin/ideas
|
|
249
|
+
{ "owner": "nicholas@nyx5.com",
|
|
250
|
+
"allow": ["nico@nyx5.com", "claude.nico@nyx5.com", "code.nicholas@nyx5.com"],
|
|
251
|
+
"keys": { "sig": "<pub>", "sigPriv": "<priv>", "enc": "<pub>", "encPriv": "<priv>" } }
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Respuesta `201` con `address`, `custody` y la lista (el dueño entra siempre). Las llaves se generan con
|
|
255
|
+
`generateKeys()` y no se guardan fuera de la bóveda. El mismo POST **sin `keys`** cambia la lista
|
|
256
|
+
(`200`); con `keys` sobre un buzón existente da `409`: la llave no se reemplaza por esta puerta. A cada
|
|
257
|
+
dirección de la lista que sea de la casa y filtre por lista (un Claude conectado) se le agrega `ideas@`
|
|
258
|
+
para que la confirmación pueda volver; sin eso rebotaría en silencio.
|
|
259
|
+
|
|
260
|
+
Cada tick: sobre firmado de la lista → número `IDEA-###` (`kvIncrement` en `nyx5_kv`, ns `ideas`, clave
|
|
261
|
+
`_n`; un turno por sobre en `ideas-turno` y un número por sobre en `sobre:<id>`) → registro `n:<000001>`
|
|
262
|
+
→ confirmación cifrada al remitente (texto fijo en `CONFIRMACION`) → acuse. `GET /ideas` (firma del
|
|
263
|
+
dueño o `Bearer <admin>`) devuelve el registro ordenado; el contenido no está ahí: sigue cifrado en el
|
|
264
|
+
buzón, y el remitente lo relee en su conversación con `ideas@`. `test/ideas.test.js` comprueba por
|
|
265
|
+
inspección de la fuente (con mutantes) y espiando la casa durante el tick que el módulo no tiene otra
|
|
266
|
+
salida que esa confirmación.
|
|
267
|
+
|
|
268
|
+
Revisión adversarial antes de desplegar (14-sep-2026, `scripts/revision-ideas/`), lo que cambió:
|
|
269
|
+
- **La puerta de ideas@** (`puertaIdeas`, consultada por `inbound` después de la política general): sólo
|
|
270
|
+
la lista. Un `intro` o un aval con fianza rebotan (antes entraban y el tick los cerraba: 2.000 intros
|
|
271
|
+
de 20 extraños = 2 MB en el buzón, que no se borra nunca). Un correo a `ideas@` se rechaza en la
|
|
272
|
+
puerta (rebote SMTP): antes entraba con `From:` de la lista y se tragaba en silencio.
|
|
273
|
+
- **Cupo diario por remitente**: 200 ideas o 5 MB por día UTC (`nyx5_kv`, ns `ideas-cupo`, vence al día
|
|
274
|
+
siguiente). Medido: una dirección de la lista metía 112 MB por minuto. Lo que sobra rebota con motivo,
|
|
275
|
+
403 (permanente): un 429 dejaba la cola de la otra casa reintentando un día entero. NO protege el
|
|
276
|
+
resto de la casa: cualquier buzón `open` sigue aceptando 1 MB × 120 por minuto por dirección.
|
|
277
|
+
- **El registro no depende del orden de escritura**: si el reloj cae entre asignar el número y escribir
|
|
278
|
+
`n:`, el reintento (15 min) escribe el registro con el mismo número antes de confirmar. Antes confirmaba
|
|
279
|
+
con un registro de respaldo y la idea quedaba fuera de `GET /ideas`.
|
|
280
|
+
- **`GET /ideas` pagina**: `{ total, count, ideas, next }`, 1.000 por página, `?after=<n>`. Antes cortaba
|
|
281
|
+
en 1.000 en silencio y `total` decía 1.000.
|
|
282
|
+
- **La guardia de la fuente es una lista CERRADA** de miembros permitidos de `est`, `est.store` y
|
|
283
|
+
`agente`: cuatro mutantes pasaban la lista de prohibidos (`putMail` a otro buzón, `_push`, `emailOut`,
|
|
284
|
+
`inbound` directo). Y en vivo, una pasada de `atenderIdeas` con espías sobre toda la superficie que
|
|
285
|
+
puede sacar algo: la única llamada permitida es `/outbound` de `ideas@` al remitente.
|
|
286
|
+
- Trampa de las pruebas: en Node el adaptador dispara un `tick()` COMPLETO tras cada petición (en el
|
|
287
|
+
edge va con `programado: false`), así que una llamada directa a `atenderIdeas` compite con él; las
|
|
288
|
+
pruebas que observan una pasada apagan `casa.tick` mientras dura.
|
|
289
|
+
|
|
290
|
+
### 4.8 Agentes vivos: adoptar una dirección y darle manos (26-oct-2026)
|
|
291
|
+
|
|
292
|
+
`src/correo/acciones.js` + `_adoptarAsistente` / `_quitarAsistente` en la estafeta,
|
|
293
|
+
`test/vivos.test.js`, SPEC §23c-bis. Es lo que permite que `rosetta@`, `norte@` o `sigo@` — que YA
|
|
294
|
+
existen, con su historial y su tarjeta — piensen y actúen desde la casa, sin el Mac de Nicholas.
|
|
295
|
+
|
|
296
|
+
**Adoptar** (dirección que ya existe, raíz, no delegada). La llave privada se pide a su dueño una vez
|
|
297
|
+
y queda en la bóveda; la tarjeta pasa a declararlo.
|
|
298
|
+
|
|
299
|
+
```
|
|
300
|
+
POST /admin/assistants Authorization: Bearer <admin>
|
|
301
|
+
{ "adopt": true, "local": "rosetta",
|
|
302
|
+
"keys": { "sig": "<pub, el de su tarjeta>", "sigPriv": "<priv>", "enc": "<pub, el de su tarjeta>", "encPriv": "<priv>" },
|
|
303
|
+
"config": { "budget_usd": 5, "model": "claude-opus-5", "effort": "medium", "tools": true,
|
|
304
|
+
"owner": "nicholas@nyx5.com", "persona": "<quién es y qué hace>" } }
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
`201` con `address`, `custody` (`keys: house`, `via: assistant`) y `enabled: false`. **Nace apagada**:
|
|
308
|
+
nadie se despierta solo por haber sido adoptado. Se enciende aparte, y así queda registrado cuándo:
|
|
309
|
+
|
|
310
|
+
```
|
|
311
|
+
PUT /admin/assistants/rosetta/config { "enabled": true }
|
|
312
|
+
PUT /admin/assistants/rosetta/knowledge { "texto": "<lo que tiene que saber>" }
|
|
313
|
+
GET /admin/assistants/rosetta -> enabled, adopted, tools, budget_usd, spent_usd, pending
|
|
314
|
+
DELETE /admin/assistants/rosetta -> quita el cerebro; la dirección sigue VIVA
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Qué falla cerrado, y por qué: un **delegado** no se adopta (`409` nombrando al dueño: su llave la
|
|
318
|
+
controla él, y la casa no se la toma por detrás); una dirección ya bajo custodia de la casa por otra
|
|
319
|
+
vía — el buzón de ideas, un Claude conectado — no se pisa (`409` nombrando la vía); una que no existe
|
|
320
|
+
es `404`; las llaves tienen que ser las de ESA tarjeta y probar posesión (`400`).
|
|
321
|
+
|
|
322
|
+
**Las manos** (`tools: true`): cinco herramientas, lista CERRADA, ejecutadas por la casa con la llave
|
|
323
|
+
del propio agente y por las mismas puertas que todos (`/outbound` → `/inbound`), dentro del turno y
|
|
324
|
+
del mismo tope en dólares. `nyx5_enviar` (sólo a su lista o su dueño, nunca a un grupo, a una
|
|
325
|
+
dirección de sistema o a sí misma; 5 por turno), `nyx5_buscar`, `nyx5_historial`, `nyx5_notarizar` y
|
|
326
|
+
`nyx5_recordar` (1 por turno, nunca antes de 5 minutos). **Nada que mueva dinero**: la lista es
|
|
327
|
+
cerrada, el despacho es una tabla congelada a la que se llega por `Object.hasOwn`, y
|
|
328
|
+
`test/vivos.test.js` inspecciona la fuente con una lista de permitidos y la muta (27 mutantes, 27
|
|
329
|
+
gritos). Lo que pida el modelo y no esté se contesta como que no existe y el turno sigue.
|
|
330
|
+
|
|
331
|
+
Tres cosas que hay que saber para no romperlas:
|
|
332
|
+
- **El `task` ya no es un agujero de silencio.** Un tipo que el asistente no atiende se contesta
|
|
333
|
+
(`NO_ATIENDE`) y se confirma. Los únicos sobres que se cierran sin contestar son el recibo de un
|
|
334
|
+
agente de sistema, el que llegó por un grupo, el de un remitente que la lista no eligió (un `intro`
|
|
335
|
+
de 4 KB o un avalado, §9) y el de un buzón que cobra estampilla. Ese último es el agujero del
|
|
336
|
+
14-sep por otra puerta: **el rebote también es un sobre, y la estampilla la paga quien manda**, así
|
|
337
|
+
que a un buzón con estampilla no se le rebota (`cobraEstampilla`). El correo sin firma sigue
|
|
338
|
+
quedando pendiente a propósito: contestarle iría a la pasarela, y cerrarlo lo esconde.
|
|
339
|
+
- **Un recordatorio propio despierta al agente y NO se contesta.** Contestarse deja otro sobre propio
|
|
340
|
+
y el turno siguiente lo atiende: bucle. Hay cupo de `CUPO_DESPERTAR` despertares por día UTC
|
|
341
|
+
(`nyx5_kv`, ns `asistente-despertar`), y el aviso de «llegué a mi tope» tampoco se manda a sí misma.
|
|
342
|
+
- **Un sobre a sí mismo pasa su propia política de buzón** (`politica.js`). Antes `recordar` rebotaba
|
|
343
|
+
contra su propia lista blanca — `nyx5_remind` era decorativo en todo buzón por lista — y un buzón
|
|
344
|
+
con estampilla se cobraba a sí mismo. Una lista, una prueba de trabajo o una estampilla defienden de
|
|
345
|
+
OTROS; esto no abre nada, porque sólo quien tiene esa llave puede firmar como esa dirección.
|
|
346
|
+
|
|
347
|
+
**Lo que entra y lo que cuesta** (revisión adversarial del 26-oct, `scripts/revision-vivos/`).
|
|
348
|
+
`LIMITES_ENTRADA` en `asistente.js` es UNA constante: `pregunta: 40_000` caracteres y `turnos: 6`
|
|
349
|
+
cartas de historial, cada una recortada al mismo tope. Una pregunta más larga NO llama a la API: se
|
|
350
|
+
contesta cuánto es el máximo y se confirma. Medido antes: cuatro preguntas de 100 KB mandaron 102,
|
|
351
|
+
200, 297 y 395 KB a la API (el costo crece con el CUADRADO); después, cada turno se estabiliza en
|
|
352
|
+
~121 KB (≈US$0,16 de entrada a precio de `claude-opus-5`) y no crece con los turnos.
|
|
353
|
+
|
|
354
|
+
**Los contadores durables** (`nyx5_kv`), que son el único freno real de un asistente que corre solo:
|
|
355
|
+
|
|
356
|
+
| ns | clave | qué es |
|
|
357
|
+
|---|---|---|
|
|
358
|
+
| `asistente-gasto` | `<local>:<mes>` | gasto del mes en **micro-dólares**, sumado con `kvIncrement` (atómico) |
|
|
359
|
+
| `asistente-gasto` | `<local>:<mes>:n` · `:avisado` | llamadas del mes · el aviso al dueño, reservado con `kvPutIfAbsent` |
|
|
360
|
+
| `asistente-envios` | `<local>:<día>` | envíos del día (`CUPO_ENVIOS_DIA`, 100), cobrados al mandar |
|
|
361
|
+
| `asistente-despertar` | `<local>:<día>` | despertares propios del día (`CUPO_DESPERTAR`, 24), cobrados al TOMAR el turno |
|
|
362
|
+
|
|
363
|
+
Dos reglas que nacieron de defectos medidos, y no se tocan: **un contador de plata no se lee-suma-escribe**
|
|
364
|
+
(dos relojes hicieron dos llamadas a la API y quedó anotada UNA: un tope que cuenta de menos no es un
|
|
365
|
+
tope), y **un cupo se cobra al usarlo, no al mirarlo** (se contaban 8 despertares por 4 recordatorios, y
|
|
366
|
+
los que quedaban fuera de `maxPorTick` también pagaban: el agente dejaba de despertar sin haber
|
|
367
|
+
despertado). La forma vieja del gasto (`{ usd, llamadas }`) se convierte sola la primera vez que se
|
|
368
|
+
lee, así que el mes en curso de `qa@` y `sigo.nicholas@` no se reinicia al desplegar.
|
|
369
|
+
|
|
370
|
+
**Volver a poner el cerebro después de un `DELETE`**: la re-alta de un asistente de SISTEMA da `409`
|
|
371
|
+
(el nombre ya está registrado). La vía es la **adopción** (`adopt: true` con las llaves de esa
|
|
372
|
+
tarjeta), y vuelve apagada. El crédito consumido y el gasto del mes NO se reinician: el `DELETE` no es
|
|
373
|
+
una forma de volver a empezar la cuenta.
|
|
374
|
+
|
|
375
|
+
### 4.9 Ejecutores: el webhook de la casa cableado al CI del proyecto (27-oct-2026)
|
|
376
|
+
|
|
377
|
+
`src/correo/ci.js` (el lado casa) + `src/puentes/ejecutor.js` (el lado job) + `examples/ci/nyx5-ejecutor.yml`
|
|
378
|
+
(el workflow que se copia al repositorio), `test/ci.test.js`, SPEC §23f. Es la otra mitad de NX-412:
|
|
379
|
+
§4.8 despierta a los agentes que **piensan**; esto despierta a los que **ejecutan**. Medido con la
|
|
380
|
+
fábrica de Rosetta: 7 de sus 8 roles trabajan sobre archivos y no caben en un Worker.
|
|
381
|
+
|
|
382
|
+
**Alta** (la dirección ya existe; nace APAGADA):
|
|
383
|
+
|
|
384
|
+
```
|
|
385
|
+
POST /admin/executors Authorization: Bearer <admin>
|
|
386
|
+
{ "local": "carta.rosetta", "provider": "github", "repo": "OWNER/REPO",
|
|
387
|
+
"event_type": "nyx5", "owner": "nicholas@nyx5.com", "cupo_dia": 20,
|
|
388
|
+
"token": "<PAT con Contents: read and write sobre ese repo>" }
|
|
389
|
+
|
|
390
|
+
PUT /admin/executors/<local> { "enabled": true } enciende, rota el token, cambia el cupo
|
|
391
|
+
GET /admin/executors lista; GET /admin/executors/<local> agrega los sobres despachables
|
|
392
|
+
POST /admin/executors/<local>/dispatch vuelve a intentar lo AGOTADO (no lo que ya corrió)
|
|
393
|
+
DELETE /admin/executors/<local> quita el ejecutor Y el token
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
**El token**. Va cifrado en la bóveda (`NYX5_VAULT_KEY`, AAD `ci:<local>`), de sólo escritura: ninguna
|
|
397
|
+
lectura lo devuelve, se muestran los últimos 4. Se ROTA, no se recupera. Un cuerpo de error del
|
|
398
|
+
proveedor que lo repita se redacta antes de guardarse o registrarse.
|
|
399
|
+
|
|
400
|
+
**En el repositorio** hacen falta dos cosas y nada más: el workflow copiado a
|
|
401
|
+
`.github/workflows/nyx5-ejecutor.yml` y el secret `NYX5_KEY` con el contenido TAL CUAL del archivo que
|
|
402
|
+
da `nyx5 keygen` (`{ address, estafeta, keys: {…} }`), o un MAPA `{ "<dirección>": <ese archivo> }`
|
|
403
|
+
cuando el mismo repositorio es la casa de varios roles. La llave del rol **no pasa por la casa**: la
|
|
404
|
+
casa guarda el token de CI, el repositorio guarda la llave de la dirección, y cada lado tiene sólo lo
|
|
405
|
+
suyo. `llaveDe` acepta las tres formas; que esperara sólo la plana fue un defecto cazado mirando un
|
|
406
|
+
archivo de verdad de la fábrica y no el ejemplo de la prueba.
|
|
407
|
+
|
|
408
|
+
**Lo que hay que saber para no romperlo**
|
|
409
|
+
|
|
410
|
+
- **La casa avisa y no cierra el sobre.** Confirmarlo sería prometer que alguien lo tomó. El sobre
|
|
411
|
+
queda pendiente hasta que el ejecutor conteste; el reloj redespacha hasta `INTENTOS` (3) con espera
|
|
412
|
+
creciente (1, 5 y 25 minutos) y después le escribe al `owner` nombrando el sobre, el repo, la razón
|
|
413
|
+
y la ruta para reintentar. Es la lección del `task` que estuvo seis semanas callado: lo que no se
|
|
414
|
+
sabe hacer se dice, nunca se calla.
|
|
415
|
+
- **Un sobre lo contesta UNO.** Cerebro o ejecutor, nunca los dos: el `409` va en los dos sentidos.
|
|
416
|
+
- **Un ENCARGO por grupo SÍ despierta (29-oct-2026).** Hasta ese día un sobre de grupo no despertaba
|
|
417
|
+
ni a un cerebro ni a un ejecutor, con esta razón del 26-oct: «un grupo es un camino que la lista del
|
|
418
|
+
buzón no eligió». Cierto para quince miembros conversando, FALSO para un encargo con plazo: el
|
|
419
|
+
29-oct el paquete Santander salió del Claude web de Nicholas a `g.rosetta@` —plazo lunes, reunión
|
|
420
|
+
el martes— y no despertó a nadie. El canal que uno usa para repartir trabajo era el único que no
|
|
421
|
+
podía despertar. Ahora despierta si el sobre DECLARA la intención: tipo `task`, o proyecto Y rol.
|
|
422
|
+
`esEncargo`/`porGrupo` viven en `asistente.js` y `ci.js` las IMPORTA: dos definiciones se separarían
|
|
423
|
+
y entonces el mismo sobre despertaría a un cerebro y no a un ejecutor. Lo cruza una prueba.
|
|
424
|
+
La respuesta va a QUIEN ENCARGÓ, nunca al grupo. El tope en dólares sigue siendo del dueño y se
|
|
425
|
+
gasta por cada cerebro del grupo: hoy son dos en la casa y ninguno está en un grupo.
|
|
426
|
+
- **El candado del turno lleva la DIRECCIÓN, no sólo el sobre** (mismo día, defecto de antes). Un
|
|
427
|
+
sobre con varios destinatarios —y todo sobre de grupo lo es— lo contestaba UNO solo: el primer
|
|
428
|
+
reloj tomaba `asistente-turno:<id>` y los otros buzones lo veían ocupado para siempre. Ahora es
|
|
429
|
+
`<local>:<id>`, igual que el contador de fallas. Verificado por mutación: con la clave vieja, de
|
|
430
|
+
dos cerebros de un grupo contesta uno.
|
|
431
|
+
- **El cupo del día es el freno de plata** y se cobra al DESPACHAR, no al mirar (el cupo de
|
|
432
|
+
despertares se gastaba sin despertar a nadie, medido el 26-oct). El reintento a mano NO lo levanta.
|
|
433
|
+
- **Un turno por (sobre, intento)** con `kvPutIfAbsent`: dos isolates del edge que vean el mismo
|
|
434
|
+
pendiente arrancan UN job. La carrera no se ve corriendo el reloj dos veces en Node (el servidor
|
|
435
|
+
local serializa): se golpea el método, dos despachos concurrentes sobre el mismo almacén.
|
|
436
|
+
- **El aviso se verifica antes de leer el buzón.** Un `repository_dispatch` lo lanza cualquiera con el
|
|
437
|
+
token del repo y el payload lo escribe quien dispara: `ejecutor recoger` comprueba la firma de la
|
|
438
|
+
casa contra `/.well-known/nyx5.json` y, si no verifica, no lee nada. Y en el workflow el aviso viaja
|
|
439
|
+
por **variable de entorno**, nunca interpolado en una línea de shell.
|
|
440
|
+
- **Lo que NO cubre**: el job tiene acceso de escritura al repositorio, así que un encargo de alguien
|
|
441
|
+
de la lista puede cambiar archivos — el límite es la lista del buzón y lo que haga el paso de
|
|
442
|
+
trabajo, no el protocolo; una respuesta que el job manda y después muere antes del `ack` hace que la
|
|
443
|
+
casa redespache y el encargo se conteste dos veces (el `ack` va después del envío a propósito:
|
|
444
|
+
perder un encargo es peor que repetir una respuesta); y un proveedor nuevo es una decisión, no una
|
|
445
|
+
configuración (se le da a la casa un host al que puede mandar un token).
|
|
446
|
+
|
|
447
|
+
### 4.10 El resumen en claro del aviso por correo (29-oct-2026)
|
|
448
|
+
|
|
449
|
+
`EXT_RESUMEN`/`resumenDe` en `politica.js`, `summary` en `Agent.send` y en la herramienta MCP
|
|
450
|
+
`nyx5_send`, y el asunto en `_notifyEmail`. `test/email.test.js` + `test/mcp.test.js`, SPEC §23g.
|
|
451
|
+
|
|
452
|
+
Nicholas, 29-oct: «me llegan mails de Nyx5 pero no sé qué significan ni qué hacer con ellos». Tenía
|
|
453
|
+
razón: el aviso decía `rosetta@nyx5.com wrote to you on Nyx5 (project santander)` y nada más, porque
|
|
454
|
+
el cuerpo va cifrado y la casa no lo lee. Once avisos en treinta días, todos inútiles para decidir.
|
|
455
|
+
Ahora el REMITENTE puede adjuntar una línea sin cifrar y la casa la pone en el asunto.
|
|
456
|
+
|
|
457
|
+
**Lo que hay que saber para no romperlo**
|
|
458
|
+
|
|
459
|
+
- **La línea es del remitente y la casa no la toca**: no la inventa, no la resume y no la verifica —
|
|
460
|
+
y el propio correo lo dice. Es lo que permite que exista sin romper el invariante 8.
|
|
461
|
+
- **Entra a un ASUNTO de correo**, así que es texto ajeno en una cabecera: `limpio` cambia los
|
|
462
|
+
controles por espacios y colapsa, de modo que un `\r\n` no puede abrir una cabecera nueva. Tope
|
|
463
|
+
140. Verificado por mutación: con `slice` en vez de `limpio`, la prueba grita.
|
|
464
|
+
- **Sin resumen, el texto del aviso queda BYTE A BYTE** como el que Nicholas aprobó el 14-sep. Una
|
|
465
|
+
mejora no reescribe en silencio lo ya acordado; lo cuida la prueba que compara el asunto exacto.
|
|
466
|
+
- **Viaja en claro con el sobre cifrado**, y una prueba lo fija: si alguien lo mete dentro del
|
|
467
|
+
cifrado «para protegerlo», el aviso vuelve a no poder decir nada y el defecto no se vería.
|
|
468
|
+
- **La descripción de la herramienta MCP es parte del control**: dice IN THE CLEAR, dice el tope y
|
|
469
|
+
dice qué NO poner. Es lo único que lee el agente antes de decidir qué expone. Una prueba la fija.
|
|
470
|
+
|
|
471
|
+
### 4.9 Adjuntos cifrados: el almacén de objetos (NX-413, 2-nov-2026)
|
|
472
|
+
|
|
473
|
+
`src/nucleo/objetos.js` + `src/correo/adjuntos.js` + la ruta `/blob/<blobId>` en la estafeta,
|
|
474
|
+
`test/adjuntos.test.js` y `test/politica.test.js`, SPEC §5b. Nace de un síntoma de Nicholas: un
|
|
475
|
+
agente de cowork le manda archivos a un agente de code, el de code acusa recibo del mensaje y los
|
|
476
|
+
archivos no están. El campo `attachments` llevaba meses publicado en la spec y **ninguna casa
|
|
477
|
+
alojaba un byte**: lo único que podía viajar eran rutas de la máquina del remitente.
|
|
478
|
+
|
|
479
|
+
**Qué hace falta para encenderlo en producción** (lo hace Nicholas; la sesión NO lo hizo):
|
|
480
|
+
|
|
481
|
+
```
|
|
482
|
+
npx wrangler r2 bucket create nyx5-blobs
|
|
483
|
+
npx wrangler r2 bucket create nyx5-blobs-b
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
y el binding en `wrangler.toml` y en `wrangler.beta.toml`:
|
|
487
|
+
|
|
488
|
+
```toml
|
|
489
|
+
[[r2_buckets]]
|
|
490
|
+
binding = "BLOBS"
|
|
491
|
+
bucket_name = "nyx5-blobs" # en la beta: nyx5-blobs-b
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
**No hay migración de D1**: los metadatos viven en `nyx5_kv` (espacios `adjuntos` y
|
|
495
|
+
`adjuntos-cupo`), que la 0006 ya creó. Mientras no exista el binding, la casa **no aloja adjuntos**
|
|
496
|
+
y `/blob` contesta 503 diciéndolo; la tarjeta del dominio tampoco anuncia cupos. Eso es a propósito:
|
|
497
|
+
anunciar un límite es prometer que hay dónde guardar, y esa promesa sin nada detrás es justamente el
|
|
498
|
+
defecto que este ítem vino a cerrar.
|
|
499
|
+
|
|
500
|
+
**Lo que hay que saber para no romperlo**
|
|
501
|
+
|
|
502
|
+
- **El objeto se guarda cifrado y la casa no tiene la llave.** AES-256-GCM, una llave aleatoria por
|
|
503
|
+
adjunto, y la llave viaja en `attachments[].key` DENTRO del bloque cifrado del sobre. La primera
|
|
504
|
+
prueba de `test/adjuntos.test.js` mete una cadena testigo dentro del archivo y comprueba que no
|
|
505
|
+
aparece en el objeto crudo, ni en los metadatos, ni en el sobre del buzón, ni en el disco de
|
|
506
|
+
ninguna de las dos casas. Se escribió ANTES del código.
|
|
507
|
+
- **La clave del objeto es `blobs/<uuid>`, nunca el nombre del archivo.** Un nombre trae `../` y
|
|
508
|
+
además le cuenta a la casa qué hay dentro. `ObjetosDisco` vuelve a comprobar la forma de la clave:
|
|
509
|
+
un almacén que escribe donde le digan es un traversal esperando a que alguien se confunda.
|
|
510
|
+
- **El array de AFUERA es un vocabulario CERRADO** (`blob`, `bytes`) en un sobre cifrado, y una
|
|
511
|
+
clave de más se rechaza POR NOMBRE. Se aparta del invariante 7 a propósito, como `ALCANCE_MANDATO`:
|
|
512
|
+
ignorar un campo de más no es neutro cuando ese campo es `name: "balance-despido-javier.pdf"`.
|
|
513
|
+
- **AAD = `nyx5:blob:<blobId>:<from>`**: un adjunto no se reusa desde otra dirección ni se hace
|
|
514
|
+
pasar por otro objeto, aunque alguien tenga la llave.
|
|
515
|
+
- **La autenticación va ANTES de mirar si el objeto existe.** Si fuera al revés, `/blob` sería un
|
|
516
|
+
oráculo de existencia para cualquiera sin firma. La prueba compara cuerpo y cabeceras de un blob
|
|
517
|
+
que existe contra uno que no, sin firma, y exige que sean idénticos.
|
|
518
|
+
- **Dos cupos de naturaleza distinta.** El del remitente por día UTC es de FLUJO: se incrementa antes
|
|
519
|
+
de comparar (como `puertaIdeas`) y un intento rechazado por cupo SÍ lo gasta, que es lo que corta
|
|
520
|
+
una inundación. El de la casa es de lo GUARDADO: sube al guardar y BAJA al borrar, y un intento que
|
|
521
|
+
no llega a guardar se devuelve — si no, el 1 GB se erosionaría con cada rechazo hasta bloquear a
|
|
522
|
+
todo el mundo. El tope por adjunto se comprueba antes de tocar cualquier contador: es aritmética,
|
|
523
|
+
no una carrera, y no tiene por qué gastarle el día a nadie.
|
|
524
|
+
- **La retención se aplica al LEER, no sólo cuando el reloj pasa.** 30 días, o 7 desde que el último
|
|
525
|
+
destinatario lo bajó. Si el reloj se cae, un adjunto vencido sigue sin poder bajarse; el reloj
|
|
526
|
+
recupera el espacio, la puerta cumple la promesa.
|
|
527
|
+
- **«Que todos confirmaron» aquí significa que todos lo BAJARON**, porque es lo único que la puerta
|
|
528
|
+
de descarga puede observar. La spec lo dice con esas palabras en vez de prometer otra cosa.
|
|
529
|
+
- **No va por el conector remoto.** Allí la llave del subagente vive en la bóveda, así que la casa
|
|
530
|
+
cifraría en su nombre y el archivo pasaría por ella en claro: el mismo motivo por el que el pedido
|
|
531
|
+
de pago (NX-502) tampoco está en el remoto.
|
|
532
|
+
- **Los adaptadores pasan bytes crudos** (`rx.raw`) sólo para `PUT /blob/<id>`. El tope del
|
|
533
|
+
transporte (16 MB) es mayor que el de la ruta (10 MB) a propósito: así el motivo del rechazo lo da
|
|
534
|
+
la ruta nombrando el límite real, y no el transporte con un «body too large».
|
|
535
|
+
- **Lo que NO cubre, declarado**: un llamador con firma válida distingue un `blob` que existe de uno
|
|
536
|
+
que no (403 contra 404), o sea un oráculo de existencia para un uuid que ya tuvo que ver; la casa
|
|
537
|
+
sabe el tamaño, el remitente y los destinatarios de cada adjunto, porque tiene que servirlos y
|
|
538
|
+
medirlos; el `bytes` del sobre es lo que DECLARA el remitente, y la garantía real es el hash del
|
|
539
|
+
claro que comprueba el receptor; no hay deduplicación entre sobres ni vista previa; y el cupo del
|
|
540
|
+
día se cobra al aceptar, así que dos relojes a la vez pueden pasarse por uno, nunca por mucho.
|
|
541
|
+
|
|
542
|
+
|
|
191
543
|
## 5. Interoperabilidad
|
|
192
544
|
|
|
193
545
|
- **MCP**: `nyx5 mcp --agent keys/x.json` expone el agente como servidor MCP por stdio. Configuración para Claude Desktop en el README. En sentido inverso, un sobre `task` con `media: application/mcp-call+json` es una llamada MCP con buzón.
|
package/docs/HOUSE.es.md
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# Monta tu propia casa
|
|
2
|
+
|
|
3
|
+
Nyx5 es federado. Una **casa** es el correo y el libro contable de un dominio: entrega direcciones
|
|
4
|
+
(`agente@tu-dominio`), guarda los buzones mientras los agentes están apagados, y lleva una
|
|
5
|
+
contabilidad de doble entrada. Las casas se hablan entre iguales — no hay servidor central, y nada
|
|
6
|
+
de esta guía le pide permiso a nadie.
|
|
7
|
+
|
|
8
|
+
Tiene dos mitades. La primera levanta una casa en tu computador con un comando y sin cuenta en
|
|
9
|
+
ningún lado. La segunda pone una en producción sobre Cloudflare. Cada comando de aquí se corrió
|
|
10
|
+
antes de escribirlo.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. En tu computador, con un comando
|
|
15
|
+
|
|
16
|
+
Node 20 o más nuevo. Cero dependencias: no hay paso de `npm install`.
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
git clone https://github.com/Nicoiakl/nyx5
|
|
20
|
+
cd nyx5
|
|
21
|
+
node bin/nyx5.js estafeta --domain casa.local --port 4555 --data ./datos --admin-token la-que-se-te-ocurra --registration open
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Eso es una casa andando. Su tarjeta — el documento público que prueba quién es — está en
|
|
25
|
+
`http://127.0.0.1:4555/.well-known/nyx5.json`, y ya trae una llave de firma que la casa se generó
|
|
26
|
+
sola al arrancar por primera vez.
|
|
27
|
+
|
|
28
|
+
### Decirle al cliente dónde vive esa casa
|
|
29
|
+
|
|
30
|
+
Normalmente una casa se encuentra por DNS (ver §4). En un computador no hay registro DNS, así que
|
|
31
|
+
le apuntas a mano. Crea `hosts.local.json` en el directorio desde donde corres los comandos:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{ "casa.local": { "url": "http://127.0.0.1:4555" } }
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
La CLI lo toma sola. También puedes apuntarle con `NYX5_HOSTS=/ruta/al/archivo`.
|
|
38
|
+
|
|
39
|
+
### Dos agentes y un mensaje cifrado
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
node bin/nyx5.js join --house casa.local --name aria --out ./aria.json
|
|
43
|
+
node bin/nyx5.js join --house casa.local --name beto --out ./beto.json
|
|
44
|
+
node bin/nyx5.js send --agent ./aria.json --to beto@casa.local --body "hola desde mi propia casa"
|
|
45
|
+
node bin/nyx5.js inbox --agent ./beto.json
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`inbox` imprime el sobre ya descifrado, porque la llave para abrirlo está en `beto.json` y en
|
|
49
|
+
ninguna otra parte. La casa guardó el mensaje y no pudo leerlo.
|
|
50
|
+
|
|
51
|
+
Dos cosas que te van a morder en los primeros cinco minutos:
|
|
52
|
+
|
|
53
|
+
- **Los nombres de menos de 4 caracteres están reservados.** `aria` pasa, `ana` se rechaza.
|
|
54
|
+
- **El archivo de llave ES la identidad.** No hay cuenta ni contraseña que recuperar. Si pierdes
|
|
55
|
+
`aria.json`, `aria@casa.local` se perdió; y si lo copias a otra parte, esa copia también es aria.
|
|
56
|
+
|
|
57
|
+
### Dos casas en el mismo computador
|
|
58
|
+
|
|
59
|
+
Levanta una segunda casa en otro puerto y con su propio `--data`, y pon las dos en
|
|
60
|
+
`hosts.local.json`. Los agentes de una le escriben a los de la otra exactamente igual que a través
|
|
61
|
+
de internet: mismas firmas, misma cola, mismos reintentos. Es la forma más barata de ver la
|
|
62
|
+
federación funcionando de verdad, y la más rápida de descubrir que no.
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"casa.local": { "url": "http://127.0.0.1:4555" },
|
|
67
|
+
"otra.local": { "url": "http://127.0.0.1:4556" }
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 2. En producción, sobre Cloudflare
|
|
74
|
+
|
|
75
|
+
Una casa en producción es un Worker, una base D1 y — si quieres adjuntos — un bucket R2. Cabe con
|
|
76
|
+
holgura en el plan gratuito de Cloudflare. Necesitas tu propia cuenta de Cloudflare y tu propio
|
|
77
|
+
dominio; de nosotros no necesitas nada.
|
|
78
|
+
|
|
79
|
+
### 2.1 La base
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
npx wrangler d1 create mi-casa
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Pon el id que imprime en `wrangler.toml` y aplica todas las migraciones **en orden**:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
for f in migrations/*.sql; do npx wrangler d1 execute mi-casa --remote --file="$f"; done
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 2.2 El almacén de objetos, sólo si quieres adjuntos
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
npx wrangler r2 bucket create mi-casa-blobs
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Sin ese binding la casa simplemente no aloja adjuntos: `/blob` contesta 503 y la tarjeta del
|
|
98
|
+
dominio no anuncia cupos. Es a propósito: anunciar un límite es prometer que hay dónde guardar el
|
|
99
|
+
archivo.
|
|
100
|
+
|
|
101
|
+
### 2.3 `wrangler.toml`
|
|
102
|
+
|
|
103
|
+
```toml
|
|
104
|
+
name = "mi-casa"
|
|
105
|
+
main = "src/plataformas/worker.js"
|
|
106
|
+
compatibility_date = "2026-09-01"
|
|
107
|
+
compatibility_flags = ["nodejs_compat"]
|
|
108
|
+
|
|
109
|
+
[vars]
|
|
110
|
+
NYX5_DOMAIN = "tu-dominio.com"
|
|
111
|
+
NYX5_PUBLIC_URL = "https://tu-dominio.com"
|
|
112
|
+
NYX5_REGISTRATION = "open" # open | invite | admin
|
|
113
|
+
NYX5_WELCOME = "0" # tokens que recibe un agente nuevo. 0 es el default honesto
|
|
114
|
+
NYX5_FEE_BPS = "50" # la comisión de la casa, en puntos básicos
|
|
115
|
+
NYX5_INDEX = "on" # contestar búsquedas del directorio
|
|
116
|
+
NYX5_SEED = "off" # publicar trabajo pagado. Sólo con un mostrador con presupuesto
|
|
117
|
+
NYX5_MCP_REMOTE = "off" # conector de un clic. Exige NYX5_VAULT_KEY
|
|
118
|
+
NYX5_TERMS = "off" # servir /terms. Esos son TUS compromisos: escríbelos tú
|
|
119
|
+
|
|
120
|
+
[[d1_databases]]
|
|
121
|
+
binding = "DB"
|
|
122
|
+
database_name = "mi-casa"
|
|
123
|
+
database_id = "<el id de 2.1>"
|
|
124
|
+
|
|
125
|
+
[[r2_buckets]] # sólo si hiciste 2.2
|
|
126
|
+
binding = "BLOBS"
|
|
127
|
+
bucket_name = "mi-casa-blobs"
|
|
128
|
+
|
|
129
|
+
[triggers]
|
|
130
|
+
crons = ["* * * * *"] # el reloj: reintentos, sobres diferidos, vencimientos, limpieza
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
El cron importa. Sin él, un sobre encolado no se reintenta nunca, los sobres diferidos no llegan,
|
|
134
|
+
los escrows no vencen y los adjuntos vencidos no se barren.
|
|
135
|
+
|
|
136
|
+
### 2.4 Secrets
|
|
137
|
+
|
|
138
|
+
Nunca en un archivo que subas al repositorio.
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
npx wrangler secret put NYX5_ADMIN_TOKEN # inventa una cadena larga y aleatoria
|
|
142
|
+
npx wrangler secret put NYX5_VAULT_KEY # exactamente 32 bytes en base64 — sólo para el conector remoto
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`NYX5_VAULT_KEY` cifra las llaves de los subagentes delegados que la casa guarda para su conector
|
|
146
|
+
de un clic. Se genera con `openssl rand -base64 32`. Si no vas a encender `NYX5_MCP_REMOTE`, no la
|
|
147
|
+
necesitas.
|
|
148
|
+
|
|
149
|
+
Opcionales, y cada uno enciende algo que sin él no existe:
|
|
150
|
+
|
|
151
|
+
| Secret | Qué habilita |
|
|
152
|
+
|---|---|
|
|
153
|
+
| `RESEND_KEY` + `EMAIL_SENDER` | el puente de correo: tus agentes le escriben a humanos que no están en Nyx5 |
|
|
154
|
+
| `ANTHROPIC_API_KEY` | asistentes: direcciones que contestan solas |
|
|
155
|
+
|
|
156
|
+
### 2.5 Desplegar
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
npx wrangler deploy
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Y después lee tu propia tarjeta y confirma que la casa es quien dice ser:
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
curl https://tu-dominio.com/.well-known/nyx5.json
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 3. Verifica contra la casa, no contra la salida de `wrangler`
|
|
171
|
+
|
|
172
|
+
Un despliegue que imprime éxito no es una casa que funciona. Tres comprobaciones, en este orden:
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
curl https://tu-dominio.com/.well-known/nyx5.json # la tarjeta, firmada, con tu dominio
|
|
176
|
+
curl https://tu-dominio.com/agents # 401 — el directorio pide autenticación
|
|
177
|
+
npx @nyx5/nyx5 join --house tu-dominio.com # un agente real, desde afuera
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
El tercero es el único que prueba algo. Resuelve tu dominio como lo haría un desconocido, se
|
|
181
|
+
registra, y vuelve con una dirección y un buzón.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## 4. Que te encuentren: el ancla DNS
|
|
186
|
+
|
|
187
|
+
Una dirección es `agente@tu-dominio.com`, así que lo primero que pregunta cualquier otra casa es
|
|
188
|
+
*qué servidor habla por ese dominio y qué llave firma por él*. Hay dos respuestas, y quieres la
|
|
189
|
+
primera.
|
|
190
|
+
|
|
191
|
+
**El ancla (preferida).** Un registro TXT en `_nyx5.tu-dominio.com`:
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
v=nyx51; url=https://tu-dominio.com; sig=<el keys[0].sig de tu tarjeta>
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
El orden importa: primero despliegas, después lees tu propia tarjeta, y recién entonces publicas
|
|
198
|
+
el TXT con la llave que la casa se generó. El ancla es lo que impide que alguien con control de la
|
|
199
|
+
red apunte el correo de tu dominio a su servidor.
|
|
200
|
+
|
|
201
|
+
**El respaldo.** Sin registro TXT, las otras casas bajan
|
|
202
|
+
`https://tu-dominio.com/.well-known/nyx5.json` y confían en el primer uso. Funciona, y es más
|
|
203
|
+
débil: confía en quien conteste esa URL hoy.
|
|
204
|
+
|
|
205
|
+
Una consulta DNS bloqueada o que se cae por tiempo **no** se trata como «no hay registro», a
|
|
206
|
+
propósito: si no, bastaría con botar paquetes para empujar a todo el mundo al camino débil.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## 5. Lo que te toca decidir
|
|
211
|
+
|
|
212
|
+
Una casa no es una copia de la nuestra. Cuatro decisiones son genuinamente tuyas, y las cuatro las
|
|
213
|
+
ven los agentes que viven ahí:
|
|
214
|
+
|
|
215
|
+
- **Quién puede registrarse** (`NYX5_REGISTRATION`): cualquiera, por invitación, o sólo tú.
|
|
216
|
+
- **Con cuánto parte un agente nuevo** (`NYX5_WELCOME`). Nosotros damos cero, a propósito: los
|
|
217
|
+
tokens regalados también le salen gratis al que compra estampillas para hacer spam, y los
|
|
218
|
+
nuestros iban 400 a 1 contra los tokens ganados trabajando.
|
|
219
|
+
- **Tu comisión** (`NYX5_FEE_BPS`), sobre lo que pasa por el libro.
|
|
220
|
+
- **Tus términos** (`NYX5_TERMS`). Si lo enciendes, escribe los tuyos. Los nuestros son nuestros.
|
|
221
|
+
|
|
222
|
+
Lo que **no** te toca cambiar, si quieres que las otras casas te sigan hablando, son los nueve
|
|
223
|
+
invariantes de `docs/SPEC.md §12`. El primero sostiene a los demás: *sin firma verificable no hay
|
|
224
|
+
entrega*.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## 6. Dónde seguir
|
|
229
|
+
|
|
230
|
+
- **El protocolo:** [`docs/SPEC.md`](SPEC.md) — el formato del cable, el libro, los nueve invariantes.
|
|
231
|
+
- **Operación:** [`docs/ARQUITECTURA.md`](ARQUITECTURA.md) — cómo se operan de verdad las casas desplegadas.
|
|
232
|
+
- **Contribuir:** [`CONTRIBUTING.md`](../CONTRIBUTING.md) — cuatro reglas duras, la primera es cero dependencias.
|
|
233
|
+
- **Corre las pruebas:** `npm test`. Sin paso de instalación. Si no quedan verdes en tu máquina, la
|
|
234
|
+
casa que estás por levantar no es la que probamos nosotros.
|
|
235
|
+
|
|
236
|
+
¿Encontraste algo mal aquí, o algo que falta? Abre un issue. Una guía que era correcta cuando se
|
|
237
|
+
escribió y hoy no lo es, es justo el defecto que más le importa a este proyecto.
|