crescō. · vigilō
office · guardia
herramienta interna · la office

vigilō.

el que monta guardia

La office corre cinco Claudes a la vez y ninguno puede tocarte la puerta. Una sesión que te espera es trabajo detenido que no sabe pedir ayuda: se queda en ámbar dentro de una caja en Alemania mientras tú estás en otra cosa. vigilō mira las cinco pantallas por ti, te busca cuando una necesita una mano, y te deja darle esa mano sin sentarte.

Nada que te espere puede quedarse callado.
Y nada que te llame puede exigir que te sientes.

La primera mitad es el aviso. La segunda es la respuesta desde la tarjeta. Un tablero que solo mira cumple media ley — y por eso no cambia nada.

ila sala

Una hoja de contactos donde los negativos están vivos.

El chrome es lino: la pared clara de una sala de vigilancia. Las únicas superficies de tinta son las pantallas — y están encendidas, con los colores reales de cada sesión. Al tocar una, esa mini-pantalla crece hasta ocupar todo. El cambio es espacial, no un cambio de tema: no entras a otra app, te acercas a la que ya estabas mirando.

Dos maestros de verdad, ninguno estrujado. Cámbialo aquí mismo — es la misma app.

el demo · jugable
crescō. vigilō 5 sesiones · 1 te pregunta
crescocrescō41m
cresco-vps · office-adduser
te pregunta · hace 40s
Instalar build-essential para compilar node-pty
mogosMogos2h 57m
mogos-group · validate-prs-120-121
5/6 agents · 714k tokens
work-mogosMogos1h 12m
mogos-group · notification-audit
te espera · prompt vacío 3m
code6h 02m
claude-shared
ociosa · sin turno pendiente
work-crescoAmedi3h 20m
amedi · volvió al shell
caída · claude ya no corre
mogos mogos-group
sesión agrupada · 100×29 · tu Termius sigue en 178×64 · ‹ › o j/k para moverte · esc para volver

Cada dato de la tarjeta sale de un sitio real, y el que no existe no se inventa: el cliente se deriva del nombre de la sesión y, si no matchea ninguno conocido —code, por ejemplo—, la tarjeta sencillamente no lleva chip. El repo sale de pane_current_path; la tarea y el progreso, de expresiones sobre el propio snapshot; la edad, de session_created.

El teléfono

Una columna, la tarjeta es la unidad y el pulgar manda. Lo que te espera flota arriba — el orden no es el de tmux, es el de tu atención. La barra de teclas aparece al entrar a la terminal, porque un teclado de iPhone no puede manejar un TUI sin esc, tab ni flechas.

El desktop

Rejilla de auto-fill minmax(320px) — tres o cuatro columnas según la ventana — y el teclado como camino de primera clase: j/k para moverte, para entrar, 1-9 para responder, esc para volver. Nunca hay que memorizar nada: los números están dibujados en los botones.

iiel semáforo

Cinco luces, porque se contestan distinto.

El diseño original tenía cuatro. Al aceptar que se puede responder desde la tarjeta, el ámbar se partió en dos: una sesión que te pregunta trae sus opciones y se resuelve con un toque; una que te espera tiene el prompt vacío y hay que escribirle. Es el mismo color de urgencia y son dos herramientas distintas. Un estado que no cambia lo que puedes hacer no merece ser un estado.

Trabajando
Está en algo. No la toques.
el pane cambió entre snapshots · esc to interrupt · spinner
Te pregunta
Trae opciones. Un toque y sigue.
opciones numeradas · [y/N] · Do you want
Te espera
Entregó algo. El turno es tuyo.
entrega seguida de ❯ vacío ≥15s
Ociosa
Abierta, sin turno pendiente.
prompt limpio · nada entregado esperando
Caída
Volvió al shell o murió.
el proceso del pane ya no es claude

Las dos luces ámbar no se distinguen solo por el color: la que pregunta late —tiene un halo que pulsa— y la que espera es un aro hueco. El color nunca carga solo el significado; siempre lo acompañan la forma, la etiqueta y lo que la tarjeta te deja hacer.

Sin adornos

Esto es heurística por firmas de texto, no una API. vigilō lee lo que se ve en el pane, igual que lo leerías tú. Va a acertar en lo común —lo que ya vimos en las cinco sesiones reales de la caja— y va a fallar en algún caso raro.

La mitigación es de diseño, no de fe: todas las firmas viven en un solo archivo. Afinar el detector es editar una lista, no refactorizar nada. Y la frontera difícil —«te espera» contra «ociosa»— se resuelve con el tiempo transcurrido desde la última entrega, que es el dato menos ambiguo que hay.

iiiresponder sin entrar

Aprobar es editar, también en una terminal.

Es la mitad de la ley que casi ningún dashboard cumple. Saber que algo te espera y no poder contestarlo desde donde estás no es información: es ansiedad con buena tipografía. La tarjeta ámbar se abre en el sitio —sin pantalla aparte, sin salir de la grilla— y te da las tres formas de contestar, en orden de esfuerzo.

Los botones son sus opciones

Cuando Claude pregunta, ofrece opciones numeradas. vigilō las lee del pane y las dibuja tal cual: si ofrece tres, ves tres, con su número y su texto. No es un menú nuestro de «aprobar / rechazar» encima del suyo — eso mentiría en cuanto la pregunta no fuera binaria, que es casi siempre.

La guardia de obsolescencia

El fallo más caro posible: decides sobre una pregunta, y para cuando tocas el botón, esa pregunta ya se contestó sola y hay otra en pantalla. Apruebas la anterior sobre la siguiente. Por eso cada respuesta lleva la huella del snapshot que la originó; si el pane cambió mientras decidías, vigilō no manda nada y te muestra lo nuevo.

crescohuella a3f
Instalar build-essential para compilar node-pty

Y ves el eco, no lo que creíste enviar

Al mandar, la tarjeta no se pinta optimista con tu texto: espera el siguiente snapshot y te muestra lo que de verdad llegó al pane. En una terminal, la diferencia entre «lo mandé» y «llegó» es todo — un carácter que se comió el modo bracket-paste ya es otro comando.

Los atajos que tú defines

Las tres cosas que dices todo el día no deberían escribirse todo el día. continúa · commítealo · para y explícame: definidos por ti una vez, disponibles en cualquier sesión. En el teléfono son la fila sobre el teclado; en desktop, teclas numéricas. Y son texto plano que tú escribes, no comandos nuestros: si mañana quieres que uno diga «hazme el PR con la plantilla de siempre», lo editas y ya.

iventrar

La misma sesión, no una copia ni un reflejo.

Cuando el toque no alcanza, la mini-pantalla crece y adentro está la sesión real: teclado completo, shift+tab, los /comandos, el scroll, todo. El puente es node-pty contra tmux, y los colores ANSI se mapean a la paleta de la casa para que ni el interior de la terminal se sienta ajeno.

Lo que no puede pasar

Al hacer attach normal, tmux encoge la sesión al tamaño del cliente más pequeño: abrir vigilō en el teléfono te aplastaría la ventana que tienes abierta en Termius. La sesión agrupada (tmux new-session -t) es la mitad de la solución — comparte las ventanas y tiene su propio tamaño — pero no basta: desde tmux 2.9 la opción window-size vale latest por defecto, así que la ventana compartida sigue al último cliente que la tocó y el visor web te encoge igual.

Se cierra fijando window-size largest sobre la sesión efímera y nunca sobre la tuya, dentro de la misma invocación de tmux para que no exista ni un instante en el que la efímera viva sin la opción puesta.

Moverte sin volver

Dijiste que lo esencial era moverte fácil entre sesiones, así que la barra lleva ‹ › y el teclado j/k: saltas de una a otra sin pasar por la grilla. En el teléfono, deslizando. La grilla es el mapa, no el peaje.

La verificación que estaba mal

La primera comprobación de esto decía «178×64 antes y después» y era falsa. La sesión agrupada se creó con -d, detached — y así el resize nunca se dispara. El fallo solo apareció cuando un pty se adjuntó de verdad: 178×64 → 100×29, exactamente lo que el diseño juraba evitar.

Queda escrito aquí porque la lección es de diseño, no de tmux: una comprobación que no reproduce el camino real no comprueba nada. Es el criterio con el que están escritos los cierres de las seis fases — por eso el de la fase 2 no dice «la terminal funciona», dice que window_width de tu sesión en Termius devuelva lo mismo antes y después.

vel aviso

Por qué la URL tiene que cambiar.

Aquí está la condición técnica que gobierna todo el proyecto, y conviene decirla antes que nada: los navegadores exigen contexto seguro para service workers, Notification y Web Push. Una IP del tailnet por HTTP plano —http://100.x.y.z:<puerto>— no lo es. Sin HTTPS no hay notificación de ningún tipo, ni siquiera con la pestaña abierta.

La salida es limpia y no cuesta nada: tailscale serve pone HTTPS real delante, con certificado de Let's Encrypt renovado solo, y sin exponer nada a internet — eso sería funnel, que no vamos a usar nunca para un servicio que da una shell.

una sola vez, en la caja

# HTTPS del tailnet, sin abrir un solo puerto al mundo
$ tailscale serve --bg <puerto>

# queda servido en:
  https://office.<tailnet>.ts.net   ← contexto seguro ✓

Los tres niveles

  • La pestaña. El título pasa a (1) vigilō y el favicon se pone ámbar. Gratis, siempre.
  • El sistema, en el Mac. Notificación nativa de Chrome que al tocarla abre esa sesión, no la portada.
  • El teléfono. Push real — con la condición de iOS: solo existe si añades vigilō a la pantalla de inicio (16.4+). No es un capricho de Apple que podamos rodear; es la única puerta.
2:14
martes 5 de agosto
vigilō
cresco te espera
ahora

Dos reglas del aviso

vila caja

Un solo proceso, dos caminos de datos.

tu lado
Chrome · el iPhone
  • la grilla
  • xterm.js al entrar
  • PWA instalada
la frontera
tailscale serve
  • HTTPS + cert auto
  • solo tailnet
  • identidad del tailnet
la office
node · tu usuario
  • HTTP grilla · 3s
  • WS terminal · node-pty
  • systemd --user

La separación de los dos caminos es deliberada. La grilla nunca abre ptys —cinco sesiones no son cinco procesos por mirarlas— y hace polling barato cada tres segundos, así que si se cae la red se recupera sola en el siguiente tick. La terminal nunca hace polling: abre un WebSocket al entrar y lo cierra al salir.

Sin framework y sin build step: node con ws y node-pty, xterm.js servido local, un servicio systemd --user que arranca solo y sobrevive reinicios —igual que el Docker rootless de la caja—. Editas un archivo y recargas.

Multi-usuario: cada quien lo suyo

La office tiene varios usuarios. Cada uno corre su propio vigilō y ve su propio tmux: sin permisos cruzados, sin decisiones de privacidad que tomar, sin un servicio privilegiado que sea un blanco. tailscale serve —que vive a nivel de máquina— mapea una ruta por usuario a su puerto.

viiseguridad

Esto entrega una shell. Digámoslo así.

Quien alcance este servicio es tu usuario en tu caja, con todo lo que eso implica. No hay forma de suavizarlo y no vale la pena intentarlo: lo honesto es enumerar las capas que hay delante y ser explícito sobre cuál es de verdad y cuál es un cerrojo.

Lo que no vamos a hacer

viiila api

Siete superficies, y ninguna de más.

Todo lo que hace vigilō cabe en siete rutas. Las de lectura son baratas y sin estado; las de escritura son tres, y las tres son explícitas sobre lo que van a hacer.

get
/api/sessions
La grilla entera: metadata, estado y snapshot de cada sesión. Es la única que se llama en bucle — cada 3s, y devuelve todo lo que la tarjeta necesita dibujar.
get
/api/sessions/:name
Una sola, para el refresco inmediato después de responder. Existe para no pagar la grilla completa por ver un eco.
post
/api/sessions
Crear. Reusa los launchers que ya viven en la caja en vez de reinventar cómo se arranca una sesión.
del
/api/sessions/:name
Matar. Destructivo, y por eso el cliente confirma con el nombre a la vista antes de llamarla.
post
/api/sessions/:name/reply
Responder sin entrar. Recibe { text, huella } y es la única ruta que puede devolver 409.
ws
/ws/:name
La terminal. Se abre al entrar y se cierra al salir; ningún pty vive fuera de esa ventana.
put
/api/prefs
Tus atajos, los silencios y la suscripción de push. Lo único que persiste.

La forma de una sesión

El contrato que hace posible que la tarjeta dibuje sus opciones: el objeto ask existe solo cuando el estado es ask, y trae la huella dentro. El cliente no puede fabricar una respuesta sin ella porque no la tiene por otro camino.

GET /api/sessions · un elemento

{
  "name": "cresco",  "repo": "cresco-vps",  "client": "crescō",
  "created": 1785922398,   // de #{session_created}
  "command": "claude",     // de #{pane_current_command}
  "state": "ask",          // work · ask · wait · idle · dead
  "since": 40,             // segundos en este estado
  "task": "office-adduser",
  "signals": { "agents": "5/6", "tokens": "714k" },
  "snapshot": "<span class=…>⏺</span> node-pty es…",

  // solo si state === "ask"
  "ask": {
    "huella": "a3f9c1",
    "prompt": "Instalar build-essential para compilar node-pty",
    "options": [
      { "n": 1, "label": "Sí" },
      { "n": 2, "label": "Sí, y no preguntes más en esta sesión" },
      { "n": 3, "label": "No, dime qué harías distinto" }
    ]
  }
}

Y la única ruta que puede negarse

POST /api/sessions/cresco/reply

 { "text": "2", "huella": "a3f9c1" }

 200  { "state": "work", "snapshot": "…" }   // el eco, ya releído del pane
 409  { "error": "stale", "ask": { … } }  // la pregunta cambió · no se mandó nada

409 Conflict es literalmente lo que pasó: el estado del recurso cambió debajo de ti. La respuesta trae la pregunta nueva, así que la tarjeta se repinta con lo que hay ahora en vez de mandarte a recargar.

El archivo de las firmas

La promesa del capítulo del semáforo —«afinar el detector es editar una lista»— solo se sostiene si de verdad hay una lista. Es server/state-patterns.js, y exporta arrays, no lógica:

// cada array es una lista de firmas. Nada de ifs anidados: si un patrón
// sobra o falta, se edita aquí y el clasificador no se toca.
export const TRABAJANDO = [/esc to interrupt/, /[✻✽✢]/, /\d+\/\d+ agents/];
export const PREGUNTA   = [/^\s*\d+\.\s/m, /\[y\/N\]/i, /Do you want/i];
export const ESPERANDO  = [/^❯\s*$/m];

// y la única función con criterio: dónde empieza y acaba la pregunta.
export function extractAsk(pane) // → { prompt, options, huella } | null
ixlas tres decisiones

Lo que el diseño abrió y había que cerrar.

Tres huecos que no existían en el spec original y que aparecieron al aceptar que se puede responder desde la tarjeta y que el aviso te busca. Ninguno es un detalle de implementación: los tres deciden si la función sirve.

1 · La huella es del bloque de la pregunta, no del pane

Si la huella fuera un hash de la pantalla entera, un spinner girando la cambiaría cada 100 ms y la guardia rechazaría todas las respuestas. La función más cuidadosa del producto se volvería la más inútil, y por una razón invisible: el usuario vería «la pregunta cambió» sin que nada hubiera cambiado.

Por eso la huella se calcula sobre el bloque que extractAsk ya localizó —la línea del prompt más las opciones, normalizadas y sin timestamps— y no sobre el pane. Consecuencias, que son justo las que se quieren:

Y se compara contra una captura fresca, no contra la del último poll. Si el servidor validara la huella contra su caché de 3 s, la guardia tendría una ventana ciega de exactamente ese tamaño — justo el escenario que existe para cerrar. La ruta reply hace capture-pane de nuevo, extrae, compara y manda en el mismo tirón. tmux no ofrece un compare-and-swap, así que la ventana no llega a cero; pero pasa de tres segundos a unos milisegundos, y esa diferencia es toda la función.

2 · Sí hace falta guardar estado — y sigue sin guardar tu trabajo

El spec original decía «sin persistencia» y era verdad entonces. El alcance nuevo lo rompe: hay cuatro cosas que tienen que sobrevivir a un systemctl --user restart.

Qué persistePor qué no puede vivir en memoria
La suscripción de pushLa da el navegador una sola vez. Perderla es dejar de avisar en silencio — el peor modo de fallo posible para esto.
Tus atajosLos escribiste tú. Que se borren en un reinicio los vuelve inservibles.
Los silencios«Callada hasta las 4» tiene que seguir siendo verdad después de un deploy.
La última transición avisadaSin esto, un reinicio re-avisa de todo lo que ya está en ámbar. Un servicio que se reinicia solo te llenaría el teléfono.

La forma: un solo archivo JSON en ~/.local/state/vigilo/state.json, escrito con write-and-rename atómico. Sin SQLite, sin base de datos, sin migraciones. Y la regla que mantiene el juramento intacto: se guarda lo que tú configuraste y lo que ya te avisé; nunca lo que dijo Claude. Ni una línea de pane toca el disco.

3 · Cómo entra la respuesta al pane

Aquí es donde «lo mandé» y «llegó» se separan de verdad, y hay dos trampas conocidas.

# responder una opción: el menú de Claude responde a la tecla
$ tmux send-keys -t cresco -l "2"

# responder texto libre: literal + Enter aparte
$ tmux send-keys -t work-mogos -l "continúa" && tmux send-keys -t work-mogos Enter

# varias líneas: por buffer, con bracketed paste
$ tmux load-buffer -b vigilo - && tmux paste-buffer -b vigilo -p -t work-mogos

A verificar contra el TUI real

Que el menú de permisos de Claude se resuelva con la tecla del número sin Enter es lo que se observa, pero no está documentado y puede cambiar entre versiones. Se verifica en la caja durante la fase 3, y si resulta que necesita Enter, la diferencia vive en una sola función — send(sesión, texto, esTecla).

Es la segunda cosa de esta pieza marcada así, junto con las cabeceras de identidad del tailnet. Las dos se comprueban antes de que nada dependa de ellas.

xel aviso por dentro

El ciclo de una sola transición.

«Un aviso por transición, no por estado» es una regla de producto; esto es la máquina que la cumple. Todo pasa dentro del mismo bucle de 3 s que ya alimenta la grilla — el aviso no tiene proceso propio.

El poller clasificaCada 3 s, listSessions() — una sola llamada a tmux — y el clasificador sobre cada snapshot.
¿Hubo transición?Una sesión que pasa de work o idle a ask o wait. Solo eso es una transición; quedarse en ámbar no lo es.
¿Ya avisé de esta?Se compara contra state.json por sesión + huella. Si coincide, se corta aquí. Y como la clave incluye la huella, una pregunta nueva en la misma sesión sí vuelve a avisar — que es exactamente lo que quieres.
¿Está silenciada?Si now < hasta, se corta. El silencio caduca solo; no hay que acordarse de reactivarlo.
Sale el pushCon tag: "<sesión>", que hace que la notificación nueva reemplace a la vieja de esa sesión en vez de apilarse. El body dice «cresco te espera» y nada más.
El service worker abre el sitio correctonotificationclickclients.openWindow('/s/cresco'). Te deja dentro de esa sesión, no en la portada — si tocas un aviso y tienes que buscar de qué era, el aviso falló.

Las llaves

Web Push se firma con un par VAPID, generado en la instalación: la privada queda en ~/.local/state/vigilo/vapid.json con permisos 0600, la pública se sirve al cliente para pushManager.subscribe(). No hay servicio de terceros de por medio: el proceso de tu caja firma y entrega directamente al endpoint que le dio el navegador.

El borde honesto del push

La notificación viaja por Apple o Google y llega aunque el teléfono no tenga el tailnet despierto. Pero el link no. Si tocas el aviso sin Tailscale activo, la PWA abre y muestra «sin tailnet» con el botón para activarlo — no un error de red del navegador.

Es la costura real de este diseño: el llamado viaja por internet, la respuesta solo por el tailnet. Y está bien que sea así — es la misma frontera que hace que esto no sea una shell en la web.

xide aquí a que corra

Qué sobrevive, y en qué orden.

La implementación anterior llegó a la tarea 4 de 8 antes de que abriéramos esta sesión de diseño. La mayor parte del motor sirve tal cual; lo que se rehace es la superficie.

Lo construido en claude-sharedestadoQué pasa con este diseño
1 · Parsers de tmuxlistSessions() de una sola llamadasobreviveEntera. El formato ya trae path y command en la misma invocación, que es lo que hace barato el bucle de 3 s.
2 · Clasificador de estadose amplíaEl motor y el archivo de firmas se quedan. Hay que partir el ámbar en dos y añadir extractAsk(), que no existía: es lo que hace posible responder sin entrar.
3 · Snapshots y ANSI→HTMLsobreviveEntera, con el fix del byte ESC que encontró el revisor ejecutando el algoritmo contra bytes reales. Ese bug habría filtrado bytes de control al HTML de cada tarjeta.
4 · Puente node-ptysobreviveEntera. La sesión agrupada ya estaba resuelta y verificada contra la caja.
5–8 — sin empezarse rehacenSe reescriben contra este diseño: cinco estados, superficie de respuesta, transporte HTTPS y aviso.

Las seis fases

Cada una cierra con una comprobación que puede fallar de verdad. Una fase que cierra con «compila» no cierra nada.

fase 1La grilla que no miente
Rutas de lectura, los cinco estados, la tarjeta con su metadata real y los dos maestros.
cierra cuandoLa grilla muestra las sesiones vivas y el conteo del header coincide con tmux ls; una sesión esperando input sale en ámbar y una con trabajo en curso, en moss.
fase 2Entrar
WebSocket, node-pty contra la sesión agrupada, xterm.js con los ANSI mapeados y la barra de teclas del teléfono.
cierra cuandoEscribes un prompt con shift+tab y /comandos desde el navegador y llega — y tmux display -p '#{window_width}' de tu sesión de Termius devuelve lo mismo antes y después.
fase 3Responder sin entrar
extractAsk(), la huella, la ruta reply, el 409 y el eco releído del pane.
cierra cuandoRespondes desde la tarjeta y ves el eco real; y con el pane movido a propósito, aparece el 409 y se verifica en el pane —no en la UI— que no llegó nada.
fase 4La frontera
tailscale serve, la unidad systemd --user, el instalador y la resolución de la identidad del tailnet.
cierra cuandohttps://office.<tailnet>.ts.net responde, sigue en pie tras un reboot, y un curl desde fuera del tailnet no llega.
fase 5El aviso
Manifest, service worker, VAPID y el ciclo de transición completo con su clave sesión+huella.
cierra cuandoEl iPhone con la PWA instalada recibe el push de una transición real y al tocarlo cae dentro de esa sesión — y una sesión que sigue en ámbar diez minutos después no ha vuelto a avisar.
fase 6Los atajos y el silencio
El almacén JSON atómico, los atajos editables y silenciar una sesión por una hora.
cierra cuandoEditas un atajo, silencias una sesión, corres systemctl --user restart vigilo y las dos cosas siguen siendo verdad.
xiilos bordes

Lo que pasa cuando algo sale mal.

El bordeQué hace vigilō
La sesión muere mientras la mirasLa terminal lo dice y te devuelve a la grilla en tres segundos. No se queda un cadáver en pantalla fingiendo estar vivo.
El WebSocket se cae — cambias de red, duermes el MacReconexión con backoff. tmux nunca perdió nada: al volver ves el estado actual, no el que dejaste.
La red mala como estado normalLa grilla se degrada a la última foto buena con su hora visible. Un dato viejo etiquetado es honesto; un dato viejo disfrazado de fresco, no.
Tu Mac se apagaNada se pierde. Es el mismo escenario que ya vive la office hoy: el trabajo es de la caja, no de tu laptop.
El primer día, sin sesionesEl vacío no dice «no hay datos»: te ofrece crear la primera, reusando los launchers que ya existen en la caja.
Matar una sesiónConfirmación explícita con el nombre a la vista. Es destructivo y se comporta como tal.
xiiiel límite

Lo que vigilō no es.

Un producto se define tanto por lo que se niega a hacer. Estas cuatro no son omisiones pendientes: son el límite.

xivel juramento

Diez reglas que no se negocian.

vigilō · el juramento
  1. Nada que te espere se queda callado, y nada que te llame exige que te sientes. Media ley no es ley.
  2. Los botones son sus opciones, leídas del pane. Nunca un menú nuestro encima del suyo.
  3. Ninguna respuesta se manda sobre una pregunta que cambió. Si la huella no coincide, no sale nada.
  4. Se muestra el eco, no la intención. Lo que ves es lo que llegó al pane.
  5. Un aviso por transición, no por estado. Avisar de más es enseñarte a ignorar el aviso.
  6. El aviso lleva el llamado, no el contenido. Lo que sale del tailnet es una palabra, no tu trabajo.
  7. El dato que no existe no se inventa. Sin cliente conocido no hay chip; sin foto fresca, la hora a la vista.
  8. Abrir vigilō no cambia el tamaño de la ventana de nadie. Mirar no puede tener efectos secundarios.
  9. El color nunca carga solo el significado. Siempre con forma, etiqueta y lo que puedes hacer.
  10. Se dice lo que es. La detección es heurística, el token es un cerrojo, y esto entrega una shell.