Ir al contenido

La búsqueda solo está disponible en las versiones de producción. Intenta construir y previsualizar el sitio para probarlo localmente.

Seguridad y BYOK

Esta página documenta el modelo de seguridad de principio a fin: la promesa BYOK, el vault local, los sobres de handoff, la autenticación, el endurecimiento de requests y el modelo de confianza del bridge.

¿Nuevo en el lado de IA? Lee primero Conceptos de IA desde cero — los agentes actúan con herramientas sobre tu máquina/servicios, que es exactamente por qué existe todo este consentimiento, cifrado y auditoría.

“Tus API keys se cifran localmente con AES-GCM y jamás viajan a nuestros servidores.”

Esta es una afirmación contractual (visible en el copy de confianza del producto). La implementación está diseñada para que la afirmación se sostenga por construcción:

  1. Las claves viven en un vault cifrado en tu navegador (synthhires-credentials + espejo), AES-256-GCM.
  2. Para cada request, la clave se envuelve en un sobre de handoff E2EE de un solo uso y con expiración y se envía solo para esa request.
  3. El servidor desempaqueta, usa y descarta — la clave en texto plano nunca se almacena ni se registra en el servidor.

Como los agentes tienen herramientas que actúan (ver Runtimes) y como la plataforma es BYOK (las llamadas de modelo corren contra tus claves), el modelo de seguridad protege dos cosas distintas:

  • Tus claves nunca salen de tu dispositivo. El modelo se relaciona a través de un proxy que solo recibe un sobre de handoff E2EE de corta vida y de un solo uso — tu clave se desempaqueta para una request y se descarta. No es un añadido; es el contrato del que depende toda la plataforma.
  • Los agentes actúan bajo consentimiento. Un bucle autónomo llamando a desktop.shell.execute, mobile.sms.send o una API externa es genuinamente poderoso, así que cada capacidad está acotada, sujeta a consentimiento, auditada y revocable. La frontera de confianza se dibuja antes de dar la herramienta al modelo, no después.

Ambas ideas — nunca tenemos tus claves en texto plano y las herramientas solo corren con consentimiento explícito — son las que hacen aceptable dar manos a un modelo. Ver Conceptos de IALlamadas a funciones/herramientas para entender por qué existen las herramientas.

El vault local (src/lib/credentials.ts + kdf.ts)

Sección titulada «El vault local (src/lib/credentials.ts + kdf.ts)»
  • v1 (legacy): deriveLegacyV1Key() — derivación local del dispositivo. Sigue siendo legible por compatibilidad.
  • v2 (endurecida): deriveVaultKey(salt, VAULT_KDF_SECRET) — combina un salt del dispositivo (getOrCreateVaultSalt()) con un secreto proporcionado por el servidor obtenido del endpoint de vault-secret. Los blobs v2 llevan el prefijo v2:.
  • Leer antes de escribir: las escrituras v2 solo ocurren cuando el secreto del servidor es alcanzable en esta sesión; las lecturas caen a v1 para que una caída transitoria del secreto nunca “desconecte” BYOK.
  • Auto-curación: si un blob falla con la clave activa, el cliente prueba secretos históricos/dev conocidos; al acertar, marca migración y re-cifra con la clave actual en el siguiente guardado.
  • Sin borrados silenciosos: los blobs ilegibles se preservan como entradas crudas en lugar de borrarse, para que una lectura corrupta no pueda destruir datos.

El opt-in por proveedor (synthhires:vault:cloud-sync-opt-in) puede reflejar una clave en /api/vault (vault_entries en servidor). Sin ese consentimiento explícito, ninguna clave se sube jamás. migrateToServer solo toca los proveedores con opt-in.

buildVaultHandoff(provider) (credentials.ts):

  1. Obtiene el JWK público E2EE del servidor (/api/handoff-pubkey).
  2. Envuelve la clave en texto plano con la clave pública, sella issuedAt.
  3. Devuelve { provider, ephemeralPublicJwk, encryptedBlob, issuedAt } — el sobre viaja con la request de chat.
  4. El servidor descifra con su clave privada, usa la clave para la llamada al proveedor y la descarta. El sobre es de un solo uso por diseño (clave efímera por sobre).
  • Magic link + códigos OTP (src/lib/auth.ts): sign-in por email con códigos de verificación (tabla verification_codes: expiración + contador de intentos).
  • Sesiones (tabla sessions): tokens de sesión en servidor con expiración; authStore restaura desde sessionStorage de forma síncrona y reconcilia con /api/auth/me en segundo plano.
  • La puerta de auth: toda ruta API empieza con await requireResolvedAuth(cookies, request) (src/lib/auth-server.ts) — la única puerta; una línea mal ahí rompe todas las rutas (está en la lista de no-editar).
  • Modo invitado/local: los usuarios sin autenticar pueden usar la plataforma localmente; las sesiones se guardan solo en el navegador.
  • CORS / rate limits / redacción viven en src/lib/security.ts.
  • Redacción de logs: todo el logging sensible pasa por el sumidero único src/lib/log-redaction.ts — claves, tokens y PII nunca llegan a los logs en texto plano.
  • Sanitización de errores: los errores de stream pasan por safeErrorLog antes de mostrarse.
  • Retries acotados: el retry handler aplica máximo 3 intentos con backoff explícito para 429/5xx — sin bucles infinitos.

El bridge de escritorio/móvil (repo Rust externo) se conecta solo saliente:

  • Sin puertos de entrada: WebSocket 100% saliente, TLS 1.3.
  • Capacidades limitadas: desktop.shell.execute, desktop.fs.read, mobile.sms.send, … — cada una requiere consentimiento (frames consent_prompt/consent_response) y queda en el log de auditoría (device_action_log).
  • Filtro de comandos destructivos: el daemon filtra comandos destructivos antes de ejecutarlos.
  • Revocación: revocar un dispositivo mata su token (device_tokens) y cierra la conexión.
  • Emparejamiento: los códigos de emparejamiento (pairing_codes) son de un solo uso; el dispositivo prueba posesión del código antes de recibir un token.
  • Nunca debilitar el copy de confianza BYOK ni el flujo de cifrado.
  • Nunca almacenar claves de proveedores en servidor fuera de vault_entries (cifradas en reposo, solo con opt-in).
  • Nunca saltar requireResolvedAuth en una ruta API nueva.
  • Nunca registrar claves/tokens crudos — pasar por log-redaction.ts.