Saltar al contenido

Niveles y seguridad

El servidor es la única autoridad. La app nunca “ejecuta” una decisión: solo envía una intención de respuesta, y el servidor la acepta si es válida, está a tiempo y cumple el nivel exigido.

Niveles de decisión de 0 a 4El servicio pide un nivel mínimo; el workspace solo puede subirlo. Vale el nivel más alto entre las preguntas. Nivel 0 es un toque, nivel 1 dispositivo desbloqueado, nivel 2 biometría o código 2FA (la ventana del 2FA puede cubrirlo), nivel 3 biometría o código 2FA en cada acción, en línea y con un plazo corto, nivel 4 es el nivel 3 más quórum de personas.Nivel del serviciolevel: 0–4 en ask()(por defecto 1)Mínimo del workspacepuede subir,nunca bajarNivel efectivoel más alto entrelas preguntas0 · toquemarcar leído, ver1 · dispositivo desbloqueadoreiniciar contenedor2 · biometría o 2FAla ventana 2FA puede cubrirlo3 · biometría o 2FA, cada acciónen línea · plazo corto4 · nivel 3 + quórumej.: 2 de 3 aprueban (app)
Nivel Exige Ejemplo
0 Un toque Ver un panel, marcar como leído
1 Dispositivo desbloqueado (sesión iniciada) Reiniciar un contenedor, responder a un lead
2 Biometría o PIN del dispositivo, o el código 2FA Un agente de IA quiere actuar, aprobar un despliegue
3 Biometría o código 2FA en cada acción, en línea y con un plazo corto Borrar datos, dar acceso, mover dinero
4 Nivel 3 y quórum 2 de 3 directores aprueban

El servicio define el nivel en ask(). El workspace puede endurecerlo, y el servicio nunca puede bajarlo. Vale el nivel más alto entre las preguntas.

En la app nativa, la clave privada se crea en el Secure Enclave (iOS) o en el Keystore (Android) con la regla “solo firma tras autenticar al usuario”. Sin huella, rostro o código del dispositivo, el propio chip se niega a firmar.

  • El dispositivo crea una clave por membresía/servidor, así una organización revoca la suya sin afectar a las demás.
  • La respuesta firmada incluye el id del grupo de preguntas, la versión, las respuestas, la hora, un nonce y el hash del contenido mostrado.
  • La solicitud de biometría muestra un resumen de lo que se firma, para que nadie apruebe a ciegas.
  • La biometría siempre es una solicitud por decisión.

No todo dispositivo tiene lector de huellas, y el navegador no tiene biometría. Para los niveles 2 y 3, una persona puede activar la verificación en dos pasos en Cuenta con cualquier app autenticadora TOTP (Google Authenticator, Microsoft Authenticator, 1Password, Authy o la app de la propia empresa) y firmar con el código de 6 dígitos.

Firmar una decisión con el código 2FASin biometría, quien activó el 2FA firma decisiones de nivel 2 y 3 con el código de 6 dígitos de la app autenticadora. El nivel 3 siempre pide el código. En el nivel 2, si la sesión está dentro de la ventana 2FA que eligió la persona, se firma sin escribir el código; si no, la persona escribe el código, el servidor lo verifica (±30 segundos, un solo uso, 5 errores en 15 minutos bloquean) y un código válido abre la ventana para esa sesión.Abre la decisiónsin biometría aquí(navegador, PWA)¿Nivel?2 o 3Escribe el código de 6 dígitosde la app autenticadoraEl servidor verifica±30 s · un solo uso5 errores / 15 min = bloqueo¿Ventana 2FA abierta?en esta sesión,0–240 min, tú eligesRespuesta aceptadaauth_method: "totp"(código válido abre la ventana)Se firma sin códigosolo nivel 2nivel 3válidonivel 2nosí
  • El código se verifica en el servidor (±30 segundos) y vale una sola vez. 5 códigos erróneos en 15 minutos bloquean nuevos intentos.
  • La respuesta registra auth_method: "totp" y el nivel comprobado.
  • El nivel 3 siempre pide un código, en cada acción.
  • El nivel 4 (quórum) sigue exigiendo la app con biometría.
  • Desactivar el 2FA exige un código válido, así quien robe una sesión no puede desactivarlo.

Con 50 aprobaciones esperando, escribir un código en cada una no es razonable. En Cuenta, cada persona elige una ventana 2FA: desactivada (pedir siempre), 5, 15, 30, 60, 120 o 240 minutos. Tras un código válido, esa sesión firma decisiones de nivel 2 sin volver a escribir el código hasta que termine la ventana.

  • La ventana es de la sesión: otros navegadores y dispositivos necesitan su propio código.
  • Solo cubre el nivel 2. El nivel 3 siempre pide un código nuevo.
  • Desactivar el 2FA borra la ventana de todas las sesiones.
  • Cada grupo de preguntas declara offline: allowed | denied y un vencimiento.
  • El servidor rechaza una respuesta para una versión antigua o una pregunta vencida, y la app explica el motivo (“tu respuesta no se aceptó porque la pregunta venció a las 12:30”).
  • Idempotencia: la misma respuesta enviada dos veces se ejecuta una vez.
  • El nivel 3 o más nunca se acepta sin conexión.
UPDATE question_sets
SET status = 'answered', answered_by = $3, answered_at = now()
WHERE id = $1 AND version = $2 AND status = 'open';
-- 0 filas = ya decidido, vencido o versión distinta

La respuesta y el aviso a tu sistema se guardan en la misma transacción (outbox transaccional). Un proceso aparte entrega el webhook firmado con reintentos automáticos, así la decisión nunca se pierde.

La respuesta que recibe tu sistema está firmada con la clave Ed25519 del servidor. Las claves públicas están en <api>/.well-known/notificme-keys.json, y el SDK las verifica por ti. Mira Webhooks.

  • Revocación de un dispositivo perdido desde la app web (Dispositivos).
  • Auditoría: quién, qué dispositivo, qué nivel, cuándo y el hash del contenido, inmutable y exportable.
  • Los tokens de servicio tienen alcances, y los tokens de agentes de IA pueden exigir aprobación humana en cada escritura. Mira Agentes de IA y MCP.