diff --git a/README.md b/README.md index 13dfd3c..583478b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

Notas personales que viven en tu navegador y se sincronizan entre tus propios dispositivos.
- Sin cuentas, sin backend que centralice el contenido y con cifrado de extremo a extremo. + Sin cuentas, sin un almacén central de contenido y con cifrado de extremo a extremo.

@@ -18,23 +18,132 @@ Interfaz visual de Selnote

---- +## Qué es Selnote -## Qué hace +Selnote es una aplicación web estática para crear notas y conservar archivos cifrados en el navegador. Replica los cambios entre dispositivos autorizados mediante conexiones directas WebRTC y representa las notas como neuronas alrededor de un cerebro 3D. -Selnote está pensado para conservar notas y archivos bajo tu control: cifra los datos en el dispositivo, guarda una copia local y replica los cambios directamente entre dispositivos emparejados. La interfaz convierte el conjunto de notas en un mapa visual para navegar el contenido y entender qué dispositivos forman parte del cerebro. +No requiere una cuenta ni usa un backend como almacén de notas. La infraestructura de red ayuda a encontrar dispositivos y, cuando la conexión directa falla, puede transportar bytes ya cifrados. -El cambio de nombre a Selnote conserva la base de datos local y el espacio de descubrimiento usados por versiones anteriores. Actualizar la aplicación no migra ni borra las notas existentes, y los dispositivos ya emparejados continúan compartiendo el mismo protocolo. +> [!IMPORTANT] +> El enlace secreto contiene la llave que permite encontrar y descifrar el cerebro. No lo publiques ni lo compartas con personas que no deban leer su contenido. -## Funcionalidades principales +## Probar ahora -- **Notas locales cifradas** con AES-GCM y almacenamiento en IndexedDB. -- **Historial verificable de cambios**, firmado con claves Ed25519. -- **Sincronización P2P** entre dispositivos mediante WebRTC; el contenido viaja cifrado. -- **Emparejamiento por código o QR** para autorizar nuevos dispositivos. -- **Modo de transferencia por luz**: enviá notas pequeñas de pantalla a cámara sin Wi-Fi, datos ni Bluetooth. -- **Adjuntos e imágenes** cifrados, fragmentados y verificados por hash antes de usarse. -- **Edición, búsqueda, eliminación y revocación** desde una única interfaz web. +Abrí **[Selnote en GitHub Pages](https://anthoniriv.github.io/selnote-web/)** en un navegador moderno. + +1. Seleccioná **Crear cerebro**. +2. Creá una nota con el botón **+** o pegá texto, imágenes o archivos. +3. Desde **enlace secreto**, elegí **mi dispositivo** para conectar otro equipo propio o **invitado** para una sesión de lectura temporal. +4. Abrí el enlace en el segundo dispositivo y mantené ambos conectados hasta que aparezca la sinapsis. +5. Editá una nota y comprobá que el cambio llegue al otro dispositivo. + +## Inicio rápido + +### Crear notas + +- **+** abre el editor para crear una nota. +- **⧉** crea una nota desde el portapapeles; en escritorio también podés usar `Ctrl+V` o `⌘V` fuera del editor. +- **⌕** busca por título o contenido. También funcionan `Ctrl/⌘+F`, `Ctrl/⌘+K` y, si el sistema no lo reserva, `Ctrl/⌘+Espacio`. +- Arrastrar archivos sobre la aplicación abre una nota nueva con esos adjuntos. + +### Conectar dispositivos + +El enlace secreto da acceso al cerebro; el emparejamiento establece una conexión entre dos navegadores. Son pasos distintos: + +1. El owner comparte un enlace de **mi dispositivo**. +2. El nuevo dispositivo demuestra criptográficamente que posee la llave del cerebro. +3. El owner registra su permiso de escritura en la cadena firmada. + +Para equipos que ya tienen acceso al mismo cerebro, **sin internet** permite intercambiar una oferta y una respuesta WebRTC por texto o QR dentro de la misma red local. Ese código conecta los pares, pero no reemplaza el enlace secreto ni concede permisos por sí solo. + +## Mapa de funcionalidades + +### Notas e interfaz + +- Creación, lectura, edición y eliminación de notas según el permiso del dispositivo. +- Pegado directo de texto, imágenes y archivos; selector múltiple, selector móvil de fotos y vídeos, y arrastrar y soltar. +- Búsqueda por título y contenido con navegación por teclado y enfoque de la neurona encontrada. +- Detección de enlaces `http` y `https` dentro del texto, abiertos en una pestaña aislada. +- Bloques de código con sintaxis de triple acento grave, etiqueta de lenguaje y botón para copiar. +- Fecha local de la última modificación y resúmenes de contenido en las tarjetas. +- Cerebro 3D interactivo: rotación con puntero, zoom con rueda o gesto de pellizco, pulsos al recibir cambios y una rama por nota. +- Vista plana de respaldo si WebGL o la biblioteca 3D local no pueden cargarse. + +### Archivos y contenido multimedia + +- Adjuntos cifrados de hasta **2 GB por archivo**. El límite efectivo también depende de la cuota y el espacio disponible en el navegador. +- Cifrado y almacenamiento por fragmentos de 256 KB para evitar cargar el archivo completo en memoria durante la ingesta. +- Vista previa de imágenes, reproducción de audio y vídeo, y descarga de cualquier adjunto disponible. +- Verificación SHA-256 del contenido recibido antes de aceptarlo. +- Transferencias reanudables por fragmento, con deduplicación de fragmentos idénticos y reintentos cuando un emisor desaparece. +- Descarga paralela desde varios dispositivos, repartida según la velocidad observada de cada conexión. +- Barra de progreso con porcentaje, velocidad y ruta utilizada: directa, por relevo o mixta. +- Wake Lock cuando el navegador lo permite, para reducir interrupciones mientras hay una transferencia activa. + +### Sincronización y red + +- Replicación P2P por canales WebRTC; el tracker coordina la señalización, no almacena notas. +- Descubrimiento mediante un tópico derivado de la llave: sin el secreto no se conoce el canal del cerebro. +- Fallback por WebSocket cuando los pares no consiguen una ruta WebRTC directa. El relevo transporta el contenido cifrado por la aplicación. +- Sincronización de la cadena completa al detectar huecos y difusión inmediata de bloques nuevos. +- Convergencia determinista ante ramas simultáneas y reaplicación de cambios propios que hayan quedado fuera de la rama adoptada. +- Reanudación al volver desde segundo plano, limpieza de conexiones obsoletas y reintento de archivos pendientes. +- Control **desconectar/conectar** para aislar el dispositivo de la red sin dejar de usar su copia local. +- Estado visible de red, número de pares y diagnóstico básico cuando no existe una ruta entre dispositivos. +- Emparejamiento local sin salida a Internet mediante oferta/respuesta copiada, compartida o escaneada como QR. + +### Transferencia por luz + +- Envío de una nota y sus adjuntos directamente de una pantalla a una cámara, sin Wi-Fi, datos móviles ni Bluetooth. +- Secuencia de QR animados con códigos fountain: el receptor puede reconstruir la carga aunque pierda algunos cuadros. +- Límite exacto de **2 MB por envío**, incluyendo texto, metadatos y adjuntos. +- Progreso de emisión y recepción, selección automática de cámara trasera cuando está disponible y reconstrucción local como una nota nueva. + +La transferencia por luz no sincroniza dos cerebros ni concede acceso: importa una copia puntual de la nota en el cerebro receptor. + +### Dispositivos y permisos + +- Dos clases de enlace secreto: + - **Mi dispositivo:** conserva la identidad y la réplica local; solicita permiso para escribir. + - **Invitado:** funciona en memoria, es de solo lectura y olvida llaves, bloques y archivos al cerrar la pestaña. +- Identidad Ed25519 propia por dispositivo persistente; la clave privada no sale del navegador. +- Prueba cifrada de posesión de la llave antes de que el owner autorice un dispositivo nuevo. +- Panel con dispositivos autorizados, estado en línea, custodio actual y orden de sucesión. +- Revocación de escritura. El dispositivo revocado conserva lo ya sincronizado y necesita una reautorización explícita para escribir otra vez. +- Lectura, búsqueda y descarga disponibles para invitados y dispositivos sin permiso de escritura. + +### Custodia y resiliencia + +- Un único dispositivo mantiene la custodia vigente y firma las operaciones administrativas. +- Orden de sucesión firmado y editable por el custodio. +- Traspaso de custodia al primer sucesor conectado cuando el owner cierra la página. Una recarga se distingue de un cierre para no borrar la copia accidentalmente. +- Reclamación de custodia por el primer sucesor disponible después de **60 segundos** sin ver al custodio anterior. +- Purga de una réplica persistente que permanece **5 minutos** sin ninguna sinapsis; la aplicación avisa un minuto antes. +- Recuperación de una réplica local cuya cadena no pasa la verificación: se descarta la cadena corrupta y se intenta resincronizar desde otros dispositivos. +- Eliminación global iniciada por el custodio mediante una sentencia firmada. El modo lápida continúa notificando a dispositivos que estaban apagados cuando vuelven a conectarse. + +> [!WARNING] +> El modelo de custodia prioriza que la memoria viva mientras haya dispositivos conectados. Al cerrar como custodio, la copia local puede borrarse y la custodia pasar a un sucesor. Leé el diálogo de confirmación antes de cerrar o eliminar el cerebro. + +### Privacidad e integridad + +- Notas cifradas con AES-256-GCM antes de persistirse o transferirse. +- Adjuntos cifrados por fragmento y almacenados fuera de la cadena; la cadena conserva descriptores cifrados. +- Historial enlazado por hash y operaciones firmadas con Ed25519. +- Validación del génesis, continuidad de índices y hashes, firmas, autoridad vigente y permisos de escritura antes de aceptar una cadena. +- Llave de cifrado dentro del fragmento `#` del enlace: el navegador no la envía como parte de la solicitud HTTP normal. +- Política CSP restrictiva y bibliotecas JavaScript servidas desde `vendor/`, sin fallback ejecutable a CDN. + +El cifrado protege el contenido, no todo el contexto de conexión. Tracker, STUN y TURN o relevo pueden observar metadatos necesarios para establecer la comunicación, como direcciones IP, puertos, horarios, pares participantes y volumen aproximado de tráfico. Consultá [Red y despliegue](docs/networking.md) y el [modelo de amenazas](docs/threat-model.md). + +### PWA y operación + +- Manifest para instalar Selnote como aplicación independiente cuando el navegador lo permite. +- Service worker con estrategia network-first y respaldo offline para el shell: HTML, manifest, icono y bibliotecas locales. +- Apertura y uso de la réplica local sin Internet después de que el shell haya quedado en caché. +- Precarga de las bibliotecas QR necesarias para el emparejamiento local y la transferencia por luz. +- Comprobación periódica de versión en despliegues que expongan `/version`; la recarga se aplaza mientras el usuario edita o confirma una acción. +- Aplicación estática sin proceso de compilación ni dependencias npm de ejecución. ## Cómo funciona @@ -46,34 +155,39 @@ El cambio de nombre a Selnote conserva la base de datos local y el espacio de de │ ├── se guarda localmente en IndexedDB │ - └── se replica a dispositivos autorizados por WebRTC + └── se replica a dispositivos autorizados + │ + ├── WebRTC directo + └── relevo cifrado si no hay ruta directa ``` -El contenido no se sube a un servicio central. Un tracker liviano puede ayudar a encontrar pares y negociar la conexión, pero las notas y los adjuntos se envían cifrados entre dispositivos. Para un intercambio puntual, el código QR permite iniciar la transferencia usando únicamente la pantalla y la cámara. +La cadena registra la secuencia verificable de operaciones. Los binarios se guardan aparte, cifrados y direccionados por hash, para que la sincronización inicial siga siendo ligera y las transferencias puedan reanudarse por fragmentos. -## Flujo de uso - -1. Abrí la aplicación en un navegador compatible y creá un espacio de notas. -2. Guardá notas o adjuntá archivos: permanecen cifrados en el almacenamiento del navegador. -3. Emparejá otro dispositivo con el código o QR para replicar el contenido entre ambos. +El cambio de nombre a Selnote conserva el nombre heredado de la base IndexedDB y el espacio de descubrimiento del protocolo. Actualizar la aplicación no migra ni borra por sí mismo las notas existentes, y los dispositivos compatibles continúan compartiendo la misma red. ## Arquitectura El proyecto es una aplicación web estática y autocontenida: ```text -index.html interfaz, lógica de aplicación y estilos -assets/ recursos visuales de la documentación +index.html interfaz, estilos y lógica principal +manifest.webmanifest metadatos de instalación +sw.js caché del shell offline +vendor/ Three.js y bibliotecas QR locales +scripts/ validaciones usadas por la aplicación y la CI +tests/ pruebas de integridad del repositorio +docs/ red, despliegue y modelo de amenazas +assets/ recursos visuales de la documentación ``` -El archivo de entrada implementa la interfaz, IndexedDB, Web Crypto, WebRTC y la visualización 3D. El repositorio incluye un `package.json` sin dependencias de ejecución para las verificaciones locales y de CI; no requiere proceso de compilación ni backend de almacenamiento. - | Capa | Tecnología | |------|------------| | Interfaz | HTML, CSS y JavaScript vanilla | | Almacenamiento | IndexedDB del navegador | -| Cifrado e identidad | Web Crypto, AES-GCM y Ed25519 | -| Sincronización | WebRTC P2P + señalización liviana | +| Cifrado e identidad | Web Crypto, AES-GCM, SHA-256 y Ed25519 | +| Sincronización | WebRTC P2P + señalización y relevo WebSocket | +| Experiencia offline | Web App Manifest + Service Worker | +| Visualización | Three.js local con fallback plano | ## Ejecución local @@ -85,13 +199,21 @@ python3 -m http.server 8080 Abrí [http://localhost:8080](http://localhost:8080). Usar un servidor local, en vez de abrir el archivo directamente, permite que el navegador aplique correctamente las APIs web que utiliza la aplicación. -## Consideraciones +Para ejecutar las mismas verificaciones que la CI: + +```bash +npm run check +npm test +``` + +## Límites y recuperación -- El contenido se almacena localmente en cada navegador; borrar los datos del sitio elimina esa copia. -- La sincronización depende de que los dispositivos puedan conectarse como pares. -- El tracker solo facilita el encuentro y la negociación; no es el almacén de notas. Los servicios de tracker, STUN y TURN pueden observar metadatos de red necesarios para establecer la conexión —por ejemplo, direcciones IP, puertos, momento y volumen aproximado del tráfico—, aunque no el contenido cifrado de las notas ni de los adjuntos. -- Los adjuntos se cifran antes de persistirse y se verifican al recibirse. -- La transferencia por QR está pensada para notas y cargas pequeñas: el límite práctico depende de la cámara y la velocidad de lectura. +- Selnote no sustituye una estrategia de respaldo. Si se borran todas las copias del navegador o se pierde el enlace secreto, no existe un servidor central desde el que recuperar el contenido. +- Borrar los datos del sitio elimina la réplica y las llaves guardadas en ese navegador. +- La sincronización requiere que al menos dos dispositivos compatibles coincidan en línea y logren una ruta directa o por relevo. +- Los archivos admiten hasta 2 GB, pero la cuota real depende del navegador y del dispositivo. +- La transferencia por luz admite hasta 2 MB y su velocidad depende de las pantallas, las cámaras y la iluminación. +- Algunas funciones requieren permisos o soporte del navegador: portapapeles, cámara, WebRTC, Web Crypto, IndexedDB, Service Worker y Wake Lock. ## Comunidad y calidad @@ -102,10 +224,3 @@ Abrí [http://localhost:8080](http://localhost:8080). Usar un servidor local, en - [Red y despliegue](docs/networking.md) - [Modelo de amenazas](docs/threat-model.md) - [Licencia MIT](LICENSE) - -Para ejecutar las mismas verificaciones que la CI: - -```bash -npm run check -npm test -``` diff --git a/index.html b/index.html index 133662f..d3688e2 100644 --- a/index.html +++ b/index.html @@ -64,9 +64,51 @@ /* ---- setup ---- */ #setup { position: fixed; inset: 0; z-index: 100; - display: flex; flex-direction: column; justify-content: center; align-items: center; + display: flex; flex-direction: column; justify-content: flex-start; align-items: center; background: rgba(255,255,255,.82); backdrop-filter: blur(6px); - padding: 1rem; + padding: clamp(1rem, 4vh, 2.5rem) 1rem; + overflow-y: auto; + } + #setup .setup-brand { + position: relative; + width: min(720px, 92vw); + margin-top: auto; + color: var(--ink); + font-size: clamp(2.35rem, 11vw, 4.75rem); + font-weight: 700; + line-height: .95; + letter-spacing: .16em; + text-align: center; + text-indent: .16em; + } + #setup .setup-brand span { position: relative; display: inline-block; } + #setup .setup-brand span::before, + #setup .setup-brand span::after { + content: attr(data-text); + position: absolute; inset: 0; + color: var(--ink); + pointer-events: none; + opacity: 0; + animation: setup-glitch-in 680ms cubic-bezier(.16,1,.3,1) 120ms both; + } + #setup .setup-brand span::before { --glitch-x: -.09em; --glitch-x-reverse: .09em; --glitch-x-small: -.035em; --glitch-y: -.035em; } + #setup .setup-brand span::after { --glitch-x: .09em; --glitch-x-reverse: -.09em; --glitch-x-small: .035em; --glitch-y: .035em; animation-delay: 170ms; } + @keyframes setup-glitch-in { + 0%, 100% { clip-path: inset(0 0 100% 0); transform: translate(0); opacity: 0; } + 12% { clip-path: inset(8% 0 66% 0); transform: translate(var(--glitch-x), var(--glitch-y)); opacity: .78; } + 28% { clip-path: inset(62% 0 12% 0); transform: translate(var(--glitch-x-reverse), 0); opacity: .5; } + 44% { clip-path: inset(31% 0 42% 0); transform: translate(var(--glitch-x), var(--glitch-y)); opacity: .72; } + 62% { clip-path: inset(74% 0 4% 0); transform: translate(var(--glitch-x-small), 0); opacity: .34; } + 78% { clip-path: inset(18% 0 70% 0); transform: translate(0); opacity: .18; } + } + #setup .setup-tagline { + width: min(460px, 92vw); + margin: .75rem 0 1.35rem; + color: var(--dim); + font-size: .68rem; + line-height: 1.6; + letter-spacing: .08em; + text-align: center; } #setup .card { width: min(460px, 92vw); @@ -76,6 +118,12 @@ #setup h2 { font-size: .78rem; letter-spacing: .18em; text-transform: uppercase; margin-bottom: .6rem; } #setup p { font-size: .72rem; color: var(--dim); line-height: 1.6; margin-bottom: 1rem; } #setup .hint { font-size: .62rem; margin-bottom: .8rem; } + #setup .card:last-child { margin-bottom: auto; } + + @media (prefers-reduced-motion: reduce) { + #setup .setup-brand span::before, + #setup .setup-brand span::after { content: none; animation: none; } + } /* aviso de cerebro muerto */ #dead { @@ -407,6 +455,8 @@
+

+

Notas cifradas. Sincronización directa entre tus dispositivos.

Crear cerebro

Genera el génesis, tu llave de firma (owner) y la llave de cifrado AES-256. Todo el contenido viaja y se guarda cifrado — sin el enlace secreto nadie puede leer ni encontrar tu red.

diff --git a/screenshot.png b/screenshot.png index 6709a5f..e39008d 100644 Binary files a/screenshot.png and b/screenshot.png differ