You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

51 KiB

API y eventos de plataforma — propuesta v1 para Juegoland v2

Estado: borrador revisado el 5 de octubre de 2026, no implementado. La revisión introduce cambios incompatibles respecto del borrador anterior; no implica un cambio de protocolo desplegado. Se completa con el modelo, protección del menor y registro de revisión. El protocolo game.* conserva su contrato propio.

1. Convenciones y autorización

La API se implementará en Go según la dirección del usuario. Los contratos de dominio no dependen del lenguaje. Arquitectura recomienda API y servidor Go en un backend modular; portar reglas y cambiar Socket.IO por un binding WebSocket son trabajos separados, aún sin implementar. Este documento conserva el perfil Socket.IO explícito hasta versionar y probar su sustituto. Las políticas de producto concretan P1–P8 y son la referencia para plazos y admisión.

HTTP nuevo bajo /api/v2. /api anterior debe pasar por las mismas políticas durante la migración. JSON UTF-8, fechas UTC con Z, UUID para entidades de plataforma; gameId, versiones, digests y asientos siguen el formato del motor. Revisiones son enteros seguros no negativos.

Las solicitudes tienen esquemas cerrados por versión. Se rechazan campos desconocidos, claves JSON duplicadas, Unicode inválido, números no finitos y exceso de bytes/profundidad antes de ejecutar. Claves duplicadas requieren validación en el decodificador de texto del transporte: Zod/JSON Schema sobre el objeto ya parseado no las detectan. Las respuestas permiten extensiones opcionales compatibles; tipos/códigos nuevos requieren una estrategia de fallback o una nueva versión.

Requisito de acceso Significado
Público seguro Proyección para anónimo/edad desconocida, sin información social no revisada.
Sesión Principal válido; no basta con que exista una cookie.
Juego Sesión, cuenta y condiciones habilitadas, membresía/rol y política de contenido/admisión.
Chat Juego + mayoría de edad acreditada + SMS vigente + sin restricción de comunicación/bloqueo; también para lectura e historial.
Propietario Principal dueño del recurso; nunca un userId autoritativo suministrado en el cuerpo.
Seguridad Reportes, bloqueo, logout y gestión de datos siguen accesibles sin chat/SMS; las condiciones nuevas no impiden reportar o salir.

Los menores y edades desconocidas no reciben chat por ninguna ruta, socket o adaptador. Ser anfitrión, tutor o tener un token no cambia esto. Los DTO no publican teléfono, prueba de edad, condición de menor ni causa privada de restricciones de terceros.

HTTP usa cookie opaca; mutaciones y resolución de secretos exigen origen permitido y token CSRF ligado al contexto de navegador/sesión. Sockets exigen origen y CSRF en handshake además de autenticación. bootstrap emite un contexto anónimo aleatorio para vincular solicitudes iniciales; no acepta uno inventado por el cliente. Tras cambiar de identidad se rota ese contexto.

Éxito de lectura: { requestId, data }. Éxito de mutación: { requestId, commandId, duplicate, receipt, data? }; data es una proyección autorizada en ese momento, no contenido privado guardado para repetir sin reautorizar. requestId lo genera el servidor en HTTP; el requestId de consulta socket es UUID del cliente para correlación y no sustituye la traza interna.

2. Idempotencia y recibos

Mutaciones de dominio HTTP llevan Idempotency-Key UUID y issuedAt UTC en el cuerpo. Los comandos socket llevan commandId e issuedAt. Un registro común de plataforma se identifica por principal + commandId, independiente del transporte. La huella incluye operación, recurso, precondiciones, issuedAt y payload validado con JCS; excluye cookie, CSRF y datos de transporte. Los comandos game.* conservan su registro y forma propios.

No se conserva el JSON canónico en claro dentro del recibo. Si incluye OTP, teléfono o token, la huella se calcula con HMAC y clave separada para impedir comprobaciones por diccionario sobre secretos cortos. Los errores y trazas tampoco incorporan esos valores.

Orden obligatorio: autenticar, buscar recibo propio, comparar huella, comprobar autorización actual para datos de respuesta y, si es nuevo, autorizar la operación y validar precondiciones en la transacción. Una restricción de membresía posterior no impide recuperar el recibo mínimo de una salida propia. Nunca se devuelve una vieja instantánea privada al repetir un comando.

La ausencia de recibo no reserva la clave: solicitudes simultáneas se serializan mediante restricción única y la misma transacción del cambio. Repetición exacta devuelve recibo idéntico con duplicate: true; otro contenido da COMMAND_ID_REUSED sin sobrescribir el anterior. Rechazos de negocio definitivos se guardan; fallo de infraestructura, límite o autorización no consumen un comando como rechazo de negocio.

{
  "status": "applied",
  "resource": {
    "kind": "room",
    "id": "1093543c-d383-437c-a427-251d39d63e42"
  },
  "changed": true,
  "revisions": { "room": 6, "match": null }
}

Recibo rechazado: { status: "rejected", code, resource, observedRevisions }. Chat aplicado añade messageId y chatSequence, sin texto. No-op devuelve changed: false, revisión actual y ningún incremento. Un ack de creación de desafío SMS confirma que se aceptó la tarea, no que se verificó el teléfono.

Para comandos nuevos, issuedAt no puede tener más de 24 h ni estar más de 5 min en el futuro; bootstrap.serverTime permite corregir reloj. Si existe recibo, se consulta antes de caducidad/precondiciones. Los recibos de sala/chat duran tanto como sus datos; el resto al menos 24 h. Tras purgarlos, el sobre original ya caducado se rechaza en vez de aplicarse de nuevo. Una intención revisada por el usuario puede crear otro UUID; el cliente nunca cambia UUID o fecha automáticamente para reintentar.

GET /api/v2/commands/{commandId} y platform.command-status devuelven found con recibo mínimo propio, o unknown. unknown no prueba que un envío retrasado no vaya a confirmarse. Se reenvía exactamente el mismo sobre mientras sea admisible. No se expone el resultado de otro principal.

Operaciones que emiten secretos

Login, alta y recuperación no reutilizan ciegamente el mecanismo de recibos de dominio ni guardan credenciales en su huella/log. P6 fija correo/contraseña, recuperación y registro de intento de autenticación propio: respuesta cifrada recuperable durante 5 min, ligada al contexto y prueba original, sin reemitir sesiones revocadas. La sección 9 concreta flujos; faltan esquemas ejecutables y pruebas de emisión concurrente.

Alta de invitado debe vincularse al contexto anónimo emitido en bootstrap y a su intento, con unicidad para no crear dos identidades al perder la respuesta. Repetir solo puede recuperar esa emisión dentro de su ventana, no una identidad convertida posteriormente en cuenta. Una emisión de sesión recuperable usa respuesta cifrada, vinculada al contexto original, durante un máximo de 5 min; luego exige el flujo de acceso correspondiente. El invitado queda limitado a juego individual hasta completar requisitos.

Creación/rotación de invitación almacena hash para validar el token y una copia cifrada de respuesta durante 24 h para el mismo principal/comando. La consulta de estado nunca devuelve el secreto. Un reintento autorizado durante la ventana puede recuperarlo; después devuelve recibo e invitationId con secretAvailable: false. Rotar después es una intención nueva y revoca el token anterior. No se prolonga caducidad al repetir.

3. HTTP

Las tablas indican operaciones mínimas y campos relevantes. Las políticas de credenciales, supervisión y plazos personales están desarrolladas en P6–P8; el proveedor de edad, los esquemas ejecutables y procedimientos de evidencia deben concretarse antes de habilitarlas.

Sesión, cuenta y protección

Método y ruta Solicitud / datos Acceso
GET /api/v2/bootstrap serverTime, versiones, límites, sesión propia mínima, condiciones y capacidades de protección Público seguro, no-store; CSRF propio fuera de vistas públicas.
POST /api/v2/terms/accept version, accepted: true → recibo Contexto propio o sesión; versión exacta.
POST /api/v2/terms/revoke Versión/referencia de aceptación → registro revocado Propietario; invalida capacidades protegidas de todas sus sesiones.
POST /api/v2/auth/guest Nombre seguro → identidad/cookie Contexto anónimo, aceptación; sin reservar asiento.
POST /api/v2/auth/register, /api/v2/auth/login Alta pendiente / credencial → sesión con capacidades reales P6 y sección 9; nunca se acepta edad acreditada del cliente.
POST /api/v2/auth/logout Revocar sesión actual Seguridad; puede repetirse sin sesión vigente.
GET /api/v2/me/sessions, POST /api/v2/me/sessions/{sessionId}/revoke Sesiones propias mínimas / revocación Propietario; no devuelve hashes/tokens.
GET /api/v2/me Perfil y preferencias propias con revisión Propietario; separado del perfil público.
PATCH /api/v2/me/profile expectedRevision, campos permitidos, avatarMediaId? Propietario y políticas de contenido.
GET /api/v2/me/safety Estados mínimos de edad/teléfono, capacidades y safetyRevision Propietario; no expone pruebas ni número completo.
POST /api/v2/me/age-checks Inicio de comprobación → referencia de proveedor Cuenta; umbral acreditado por proveedor evaluado, nunca prueba autodeclarada.
POST /api/v2/me/phone-challenges phoneNumber, finalidad → desafío y tiempos, HTTP 202 Cuenta adulta acreditada; acción explícita, límites SMS.
POST /api/v2/me/phone-challenges/{challengeId}/verify code → estado propio Principal/sesión del desafío, vigente e intentos disponibles; código nunca en URL.
POST /api/v2/me/phone/revoke Desvinculación → capacidades actualizadas Propietario con reautenticación; corta chat.
POST /api/v2/me/exports, POST /api/v2/me/deletion-requests Solicitud → trabajo y estado Propietario, comprobación reciente de titularidad y P8; no requieren chat/SMS.

Callbacks de edad/SMS son interfaces servidor a servidor autenticadas, con audiencia/nonce, firma, vencimiento y dedupe. No existe un endpoint público para establecer verified, adult o guardian. Los códigos fallidos tienen contador atómico; reenviar no reinicia la cuota acumulada. Un SMS entregado no cambia por sí mismo la elegibilidad.

Catálogo, salas, invitaciones y medios

Método y ruta Solicitud / datos Acceso y precondición
GET /api/v2/catalog Fichas aptas y paginadas Público seguro; una proyección personalizada no entra en caché público.
GET /api/v2/games/{gameId}/versions/{version} Distribución pública, digests, estado de publicación Política de contenido; retirar no convierte en secreto lo ya publicado.
GET /api/v2/rooms?scope=public|mine Resumen paginado public seguro; mine exige sesión. Sin chat, tokens, lista infantil o estado privado.
POST /api/v2/rooms game: { id, version, packageDigest, dependencyLockDigest }, capacidad, visibilidad, nombre, configuración Juego; fija la selección exacta o rechaza PACKAGE_NOT_CREATABLE.
GET /api/v2/rooms/{roomId} RoomView Solo miembros/participantes históricos autorizados, también en salas públicas.
POST /api/v2/rooms/{roomId}/memberships invitationToken? → membresía Juego; no necesita revisión leída para ocupar plaza: validación atómica de capacidad/token/estado/protección.
POST /api/v2/invitations/resolve Token → ficha segura mínima y roomId Lectura sin consumo, CSRF y límites; no usa Idempotency-Key. Token ausente/inválido sin detalle.
GET /api/v2/rooms/{roomId}/invitations Metadatos/revisiones, nunca tokens Anfitrión autorizado.
POST /api/v2/rooms/{roomId}/invitations expectedRoomRevision, vencimiento/usos → ID/token Anfitrión en espera; valida cuotas y política.
POST /api/v2/invitations/{invitationId}/revoke expectedRevision → recibo Anfitrión actual; token deja de admitir entradas.
POST /api/v2/invitations/{invitationId}/rotate expectedRevision → nuevo secreto Anfitrión actual; revoca anterior en la misma transacción.
POST /api/v2/me/media Imagen acotada → mediaId, estado de validación Cuenta elegible; menores/edad desconocida solo presets.
GET /api/v2/me/media/{mediaId} Estado propio pending|ready|rejected Propietario; solo ready puede usarse como avatar.

La URL compartida propone /join#invite=TOKEN: el fragmento no se envía en la solicitud HTTP. El cliente lo lleva a memoria, limpia la barra mediante reemplazo del historial y lo resuelve por POST; la pantalla usa Referrer-Policy: no-referrer, Cache-Control: no-store y no carga terceros. El usuario debe reabrir su enlace si pierde el contexto antes de entrar. No se registran cuerpos con tokens.

Gestión de invitación tiene invitationRevision; no aumenta roomRevision salvo que cambie también el estado/configuración de sala. La comprobación de anfitrión se hace bajo bloqueo de sala, aunque la precondición sea sobre invitación.

Chat, avisos y seguridad

Método y ruta Datos Acceso
GET /api/v2/rooms/{roomId}/messages Página histórica, historyEpoch, cursor Chat; se aplica corte por retirada y redacciones actuales.
GET /api/v2/me/matches Historial/resultados proyectados Propietario; no copia resultado privado del motor.
GET /api/v2/me/notifications Página, inboxRevision, no leídos Propietario; sin texto de chat ni datos ocultos.
POST /api/v2/me/notifications/{notificationId}/read Lectura monotónica → recibo Propietario; incrementa bandeja solo si cambia.
POST /api/v2/me/blocks, POST /api/v2/me/blocks/{blockId}/revoke Destinatario autorizado / desbloqueo Seguridad, sesión; no revela quién bloqueó al solicitante.
POST /api/v2/safety/reports Recurso/persona, categoría, nota opcional → referencia Seguridad; sin requisito de chat/SMS; contexto de reporte para no autenticados con límites.
GET /api/v2/me/reports/{reportId} Estado propio y respuesta mínima Reportante, con verificación de titularidad.
GET /api/v2/commands/{commandId} Recibo propio found|unknown Sesión; puede consultar tras salir o perder permiso de chat, sin recibir contenido.

La consola de moderación y los derechos de acceso a evidencia son una interfaz administrativa separada, con roles y objetivos definidos en políticas y operaciones en la sección 9. Debe implementarse antes de comunicación pública. Los reportes sin cuenta no se exponen por ID adivinable: requieren el contexto de reporte emitido, sin revelar datos a terceros.

Paginación y caché

Listas por cursor opaco ligado a principal, filtros, orden y versión de política; por defecto 20 y máximo 50 elementos. Orden total por fecha de creación + UUID; navegación histórica de chat por createdSequence. No offsets ilimitados. Cursor vencido, contexto cambiado o historial redactado da CURSOR_EXPIRED y obliga a reiniciar la lista. Un cursor no es autorización.

Las listas móviles como salas públicas no prometen un snapshot transaccional entre páginas: se pueden actualizar entre lecturas; cliente deduplica IDs y refresca la primera página. Chat usa el contrato específico de la sección 6. Las respuestas privadas llevan private, no-store; catálogo público puede usar ETag según idioma/proyección segura y assets públicos inmutables por digest. La concurrencia se controla con revisiones de dominio explícitas, no con un ETag que mezcle proyecciones de usuarios.

4. Conexión y sobres Socket.IO

Eventos en namespace principal: platform:message y game:message. El segundo mantiene protocolVersion: 1 y su propio sync. Los acuses de plataforma son mensajes de aplicación; el callback de Socket.IO no reemplaza un recibo persistido.

Antes de suscribirse se envía un mensaje de negociación independiente:

{
  "type": "platform.hello",
  "requestId": "bdf2ea3d-1473-4c75-a0f6-b2e56807bc99",
  "supportedVersions": [1]
}

Respuesta platform.welcome: requestId, selectedVersion: 1, connectionId UUID, serverTime, límites y safetyRevision propia. Sin versión compatible: platform.negotiation-error con código y versiones admitidas, sin datos privados, y cierre. No se interpreta room.sync para negociar chat o avisos.

Después todos los sobres llevan platformProtocolVersion: 1 y type. Peticiones de lectura llevan requestId; mutaciones, commandId e issuedAt. Flujos llevan subscriptionId y recurso. Identificadores de suscripción son propios de conexión/principal y se invalidan al desconectar, cambiar identidad o revocar acceso.

Mensaje Dirección Campos específicos
room.sync C → S requestId, roomId. Sustituye suscripción anterior de esa sala/conexión.
room.snapshot S → C roomId, subscriptionId, revision, delivery, observedAt, room.
room.command C → S roomId, commandId, issuedAt, expectedRevision, expectedMatch, action.
room.ack S → C roomId, commandId, duplicate, receipt.
room.cursor S → C roomId, subscriptionId, revision.
room.unsubscribe C → S requestId, roomId, subscriptionId. Solo suscripción propia.
platform.unsubscribed S → C requestId, subscriptionId. Baja idempotente, sin alterar membresía.
platform.command-status C → S requestId, commandId. Sirve también para chat y mutaciones HTTP.
platform.command-result S → C requestId, commandId, status: found|unknown, receipt si encontrado.
chat.sync C → S requestId, roomId. Solo miembros actuales elegibles para chat.
chat.snapshot S → C roomId, subscriptionId, requestId, sequence, historyEpoch, messages, historyCursor. Cola reciente autorizada.
chat.send C → S roomId, commandId, issuedAt, text. Autor derivado del servidor.
chat.ack S → C roomId, commandId, duplicate, receipt.
chat.event S → C roomId, subscriptionId, eventId, fromSequence, sequence, historyEpoch, event.
chat.cursor S → C roomId, subscriptionId, sequence, historyEpoch.
chat.unsubscribe C → S requestId, roomId, subscriptionId; respuesta platform.unsubscribed.
presence.update S → C roomId, subscriptionId de sala, presenceSequence, observedAt, expiresAt, members.
lobby.changed S → C Señal sin IDs ni actividad de salas privadas; solo cambios del listado público.
notification.changed S → C inboxRevision propia; se consulta por HTTP.
platform.access-changed S → C eventId, safetyRevision propia, recursos/suscripciones invalidados y capacidades propias mínimas.
platform.error S → C code, messageKey, recovery, requestId? , commandId?, roomId?, subscriptionId?, retryAfterMs?.

El servidor no comparte suscripciones personales entre usuarios. Las invalidaciones de seguridad no dependen de recibir otro movimiento: la revocación corta directamente la entrega en servidor. Si el cliente pierde el aviso, el siguiente intento sigue rechazado.

platform.access-changed se deduplica por eventId, no solo por safetyRevision: perder membresía puede invalidar una suscripción sin cambiar la edad/teléfono de la cuenta. Toda invalidación de la suscripción actual se aplica aunque esa revisión de protección siga igual.

5. Sala, acciones y sincronización

RoomView contiene nombre/visibilidad aptos para destinatario, estado, capacidad, anfitrión, miembros visibles, configuración pública, paquete fijado, política/plazos, capacidades propias y matchRef nulo o con matchId, tupla del motor y minimumRevision. Miembros: membershipId, alias/avatar proyectados, estado, listo, plaza provisional y seatId nulo hasta inicio. No incluye token de invitación, historial de chat, teléfono/edad ni estado interno del motor. Crear fija admissionMode: individual|adult|protected y communicationMode: off|adult-verified: solo adult admite el segundo; individual tiene capacidad 1 y protected siempre es privado. Los valores se validan contra políticas y paquete, no conceden permisos por sí mismos ni revelan la edad de un participante.

Las capacidades son indicaciones evaluadas en observedAt, pueden caducar por tiempo/permiso y siempre se revalidan. No todo cambio de permiso incrementa roomRevision: una invalidación de acceso exige nuevo sync aunque esa revisión sea igual.

{
  "platformProtocolVersion": 1,
  "type": "room.command",
  "roomId": "1093543c-d383-437c-a427-251d39d63e42",
  "commandId": "04de01b1-771f-4775-9db0-3137399c464b",
  "issuedAt": "2026-10-05T16:00:00Z",
  "expectedRevision": 5,
  "expectedMatch": null,
  "action": { "type": "set-ready", "ready": true }
}
Acción Payload y permiso
set-ready ready: boolean; propio miembro en espera. Inicio automático según política.
configure Capacidad/configuración permitida; anfitrión en espera. Invalida todos los listo; juego/digest no cambian.
choose-color colorId ofrecido por la política de preparación del juego; propio miembro en espera. El adaptador valida la configuración de inicio; no infiere posibilidades por nombre de juego.
set-invite-message Texto limitado y aprobado por política de contenido; anfitrión en espera. No se muestra libremente a menores.
transfer-host targetMembershipId activo; anfitrión en espera, con efectos P2.
cancel Anfitrión en espera; cancelación explícita.
leave Miembro; en espera sale, en partida retira mediante motor. El anfitrión responsable de una partida protegida activa provoca cancelación de seguridad sin resultado competitivo según P7.
close-inactive Participante activo elegible según política y flujo del motor; cancela sin resultado competitivo.

En espera expectedMatch = null. En retirada/cierre activo es obligatorio { matchId, revision } exacto. Revisión de sala correcta con partida obsoleta da STALE_MATCH_REVISION. El servicio bloquea sala y partida y confirma el enlace al motor y su recibo en una sola transacción. El controlador no llama al protocolo de red game.command contra sí mismo ni duplica la retirada en dos registros independientes.

Barrera de sync: instalar recepción y buffer acotado, leer revisión R/proyección confirmada, emitir snapshot con delivery: { kind: "sync", requestId }, descartar buffer ≤R y publicar R+1… con delivery: { kind: "live", fromRevision }. Duplicados se descartan; huecos o overflow exigen nuevo sync. Un snapshot completo leído en R+k no se etiqueta como R+1.

La respuesta de salida puede preceder a la revocación de suscripción o viceversa; el recibo se puede consultar por identidad. Un cliente conserva cada flujo separado. Si llega final de sala con minimumRevision = 13 y juego sigue en 12, solicita game.sync y espera la vista autorizada de al menos 13. Una instantánea de juego más reciente que sala tampoco implica corrupción.

minimumRevision registra la última revisión de partida coordinada con un cambio de sala, empezando en 0; no promete ser la última jugada existente ni obliga a aumentar la revisión de sala en cada jugada.

6. Chat, redacción y recuperación

ChatMessage: messageId, authorMembershipId, authorDisplay, createdAt, createdSequence, status: visible|redacted, text. En lápida text = null; no hay copia del original en payload público. Normalización NFC, texto no vacío de hasta 500 puntos de código y 2 KiB UTF-8, sin HTML ni adjuntos. Longitudes se verifican tras normalizar y la huella idempotente usa el texto validado. Cuerpos no conformes se rechazan, no se truncan.

sequence ordena eventos, no solo mensajes. message.created crea un mensaje; message.redacted apunta a su ID, añade secuencia nueva y aumenta historyEpoch. No se reemite texto anterior a la redacción en un replay. Un bloqueo personal genera invalidación de acceso y nuevos marcadores sin contenido donde proceda; no cambia el mensaje global ni revela el autor filtrado.

Cada chat.sync empieza de nuevo con los últimos 50 mensajes como máximo, limitados además por bytes. Se instala primero la suscripción, se lee snapshot R y se liberan después eventos >R. La navegación de mensajes anteriores se hace por HTTP con historyCursor; no retiene una conexión entera mientras pagina todo el historial. La reconexión muestra esa cola reciente, sin repetir sonidos; no promete reproducción exhaustiva de todos los eventos desconectados.

Un evento vivo tiene sequence = fromSequence + 1. Hueco, cursor adelantado o epoch superior provoca nuevo sync y descarta el historial cacheado. Una página HTTP incluye historyEpoch; el cliente no mezcla una página de epoch anterior al ya observado. En servidor se revalida permiso/epoch antes de publicar una página o outbox. Así una respuesta retrasada no restaura texto que ya fue moderado.

La membresía almacena chatJoinedAfterSequence al entrar y, al retirarse, chatLeftAtSequence. Solo mensajes con createdSequence posterior al primero y no superior al segundo son legibles; el segundo es nulo mientras continúa. Se sincronizan únicamente eventos que puedan proyectarse dentro de ese intervalo, sin revelar autores/texto fuera de él. El antiguo miembro retirado consulta ese historial por HTTP si conserva elegibilidad adulto/SMS y ve redacciones posteriores; no tiene suscripción viva. Al perder elegibilidad tampoco conserva historial. La paginación aplica políticas actuales, no las de cuando se creó el cursor.

Las salas protegidas/mixtas rechazan chat.sync, envío e historial incluso para adultos con SMS. El mensaje se purga a los 30 días desde creación; el borrado aumenta historyEpoch e invalida cursores/cachés igual que una redacción. No se conserva su texto en outbox/replay fuera del período; el caso de moderación, si existe, tiene almacenamiento y permisos distintos. Un mensaje purgado puede conservar un marcador mínimo mientras el flujo siga existiendo, sin texto ni datos del autor.

7. Límites, avisos y recuperación

Valores iniciales para conformidad, anunciados en bootstrap/welcome:

Límite Valor propuesto
JSON de entrada HTTP / plataforma socket 64 KiB / 16 KiB; profundidad 32.
Salida de plataforma / página 256 KiB; reducir elementos y devolver cursor si se alcanza.
Sobre de juego 64 KiB según protocolo del motor; se valida por separado.
Suscripciones por conexión Hasta 5 salas y 5 chats; total máximo 10.
Buffer por suscripción al sincronizar 256 eventos o 1 MiB, lo que ocurra antes.
Cursores sala/chat Cada 15 s; detectar incluso la última entrega perdida.
Presencia Latido observado cada 15 s, TTL 45 s, secuencia por suscripción.
Consulta de avisos mientras hay foco Al conectar, recuperar foco y cada 30 s; inboxRevision cambia también al leer.
Chat 15 envíos/10 s por principal y 100/min por sala como valores iniciales, además de límites globales.
Avatar Hasta 1 MiB de entrada y 1024×1024 píxeles decodificados; PNG/JPEG/WebP estáticos, nueva codificación antes de publicar.

Configurar el límite agregado de Socket.IO no sustituye los límites específicos de mensaje. El servidor rechaza entradas demasiado grandes antes de ejecutar y corta clientes lentos sin crecimiento ilimitado. Una presencia caducada pasa a desconocida, no provoca derrota. Lecturas periódicas se suspenden en segundo plano; al volver se resincroniza.

Al reconectar: obtener sesión/capacidades actuales, negociar plataforma, sincronizar salas permitidas, solo chats elegibles y partidas asociadas; consultar recibos inciertos y reintentar el mismo sobre. Las proyecciones privadas se aíslan por identidad y se eliminan al cambiarla. No se restaura acceso por el socket.id antiguo ni por la recuperación automática de Socket.IO.

8. Errores y compatibilidad

HTTP usa Problem Details, RFC 9457: type URI estable, title, status, code, requestId, recovery y campos permitidos. Las URIs de problema deben publicarse con su documentación antes de lanzar; no se afirma que ya existan. No hay mensajes de excepción, payloads privados ni identificadores de cuentas ajenas en errores.

HTTP Códigos y uso Recuperación
400 / 413 INVALID_REQUEST, PAYLOAD_TOO_LARGE Corregir intención; no reintento automático.
401 SESSION_REQUIRED, SESSION_EXPIRED Reautenticar y limpiar proyecciones.
403 TERMS_REQUIRED, CHAT_NOT_ELIGIBLE, ACCOUNT_RESTRICTED, FORBIDDEN Mostrar requisitos propios o retirar acceso.
404 RESOURCE_NOT_FOUND También para recursos privados cuya existencia no se autoriza.
409 STALE_ROOM_REVISION, STALE_MATCH_REVISION, STALE_RESOURCE_REVISION, ROOM_FULL, ROOM_NOT_WAITING, PACKAGE_NOT_CREATABLE Sincronizar y revisión humana de una nueva intención.
409 COMMAND_ID_REUSED, COMMAND_EXPIRED, CURSOR_EXPIRED, TERMS_VERSION_CHANGED No cambiar UUID en automático; reiniciar consulta/flujo pertinente.
404 / 409 INVITATION_UNAVAILABLE para resolver token; INVITATION_CONFLICT para propietario con revisión obsoleta Mensaje genérico al visitante; refrescar al propietario.
429 / 503 RATE_LIMITED, TEMPORARY_UNAVAILABLE Retry-After HTTP / retryAfterMs socket; mismo comando si sigue admisible.

La falta de revisión obligatoria es 400 INVALID_REQUEST en este diseño, porque las precondiciones son campos del dominio. No se usa 428 para aceptar condiciones: RFC 6585 lo define para exigir una petición condicional (sección 3). Si en el futuro se añade If-Match, deben definirse aparte 428 por ausencia y 412 por condición no satisfecha.

Socket tiene las mismas categorías semánticas, sin fingir un estado HTTP. Rechazos definitivos de sala/chat llegan en recibo; problemas previos al procesamiento o transitorios en platform.error. CHAT_NOT_ELIGIBLE no detalla el estado de otros usuarios. Antes de negociación solo se permite platform.negotiation-error.

{
  "type": "urn:juegoland:problem:room-full",
  "title": "La sala está completa",
  "status": 409,
  "code": "ROOM_FULL",
  "requestId": "f8aa2fa1-aec8-41c8-a27b-644e999df03a",
  "recovery": "refresh-room"
}

Cambiar significado, tipo obligatorio o regla de autorización requiere revisar compatibilidad; un campo opcional de respuesta no autoriza campos nuevos de solicitud. Versiones de cliente incompatibles deben actualizarse antes de comunicar o ver un juego, incluida la API antigua. La fase siguiente produce OpenAPI, esquemas discriminados y pruebas desde una fuente común, con ejemplos de éxito, errores y carreras; este documento no declara que esos artefactos ya existan.

9. Flujos derivados de las políticas de producto

Operaciones de diseño que deben incorporarse a OpenAPI y pruebas. Los métodos/rutas siguientes sustituyen la ambigüedad del primer borrador; no se afirma que existan endpoints funcionando. Mutaciones de dominio mantienen recibos comunes; autenticación y tokens de credencial usan intentos específicos, vinculados al contexto anónimo/sesión y con respuesta secreta recuperable acotada según P6. No se incluye contraseña o token en una huella persistida de dominio.

Cuenta y recuperación

Operación Contrato mínimo
POST /api/v2/auth/register authAttemptId, correo, contraseña, versión de condiciones; crea cuenta pendiente o convierte invitado propio sin cambiar userId. Declarar mayoría no la acredita. El correo de respuesta no enumera cuentas existentes.
POST /api/v2/auth/email-verifications Solicitar envío/reenvío para contexto propio; 202 genérico, cuota y tarea deduplicada.
POST /api/v2/auth/email-verifications/complete Token del enlace + contexto; consumir atómicamente, verificar correo sin elevar edad/tutela/SMS.
POST /api/v2/auth/login authAttemptId, correo, contraseña, rememberMe; servidor decide si admite sesión larga. Credencial inválida con respuesta uniforme, sin distinguir correo inexistente.
POST /api/v2/auth/reauthenticate Prueba de credencial y segundo factor cuando corresponda; resultado ligado a sesión/finalidad, válido 5 min; no recibe reauthenticated: true del cliente.
POST /api/v2/auth/recovery-requests Correo y contexto; 202 genérico, sin iniciar sesión ni revelar estado.
POST /api/v2/auth/recoveries/complete Intento, token y nueva contraseña; consumo de un uso + credencial + revocación de sesiones/tokens + suspensión de privilegios sensibles en una transacción. Exige después login.
POST /api/v2/me/password Reautenticación, nueva contraseña; revocar otras sesiones y rotar la propia de forma atómica. No confundir cambio autenticado con recuperación por canal perdido.
POST /api/v2/me/email-changes, POST /api/v2/me/email-changes/{changeId}/complete Reautenticación, nuevo correo / token; unicidad y verificación antes de cambiar, aviso al anterior, sin transferir cuenta por correo escrito.
GET /api/v2/me/exports/{exportId} Estado propio; la descarga se emite solo tras comprobación vigente de titularidad, un uso, 24 h y sin URL/token en logs.
GET /api/v2/me/deletion-requests/{requestId} Estado propio y excepciones pertinentes; tras revocación, acceso con credencial de seguimiento específica, sin reactivar sesión social.

El contrato infantil de creación de credencial y emparejamiento requiere un esquema aparte; no se reutiliza el PIN actual. El desafío de emparejamiento se emite por responsable con vínculo activo, propósito/principal/dispositivo específicos, caduca en 5 min, admite 5 intentos y se consume una vez. Ningún dato de tutor o menor ajeno se revela antes de autorización. MFA administrativo es requisito de activación: endpoints WebAuthn/challenges, recuperación y validación de origen/RP deben especificarse antes de abrir la consola, no simularse con SMS.

Tutela, contactos y capacidades

Operación Contrato mínimo
POST /api/v2/me/supervision-requests Responsable adulto acreditado y reautenticado; referencia de proceso de evidencia y alcance. No enlaza un menor existente solo por correo/nick.
GET /api/v2/me/supervisions Vínculos propios y estado mínimo; sin consulta arbitraria por childUserId.
POST /api/v2/me/supervisions/{supervisionId}/children Vínculo de alta validado; crear cuenta tutelada, preset y políticas. La evidencia, edad exacta y credenciales no se publican en perfil.
POST /api/v2/me/supervisions/{supervisionId}/revoke Reautenticación, revisión del vínculo, motivo privado acotado; incrementar protección y revocar contactos/permisos dependientes.
POST /api/v2/me/supervisions/{supervisionId}/contact-authorizations Referencia de principal autorizado mediante intercambio privado, alcance y caducidad; no directorio público de menores. Para dos menores exige aprobación de ambos responsables.
POST /api/v2/me/contact-authorizations/{authorizationId}/revoke Titular responsable, revisión; corta nuevas admisiones y aplica cancelación de seguridad si comparten partida protegida activa.
POST /api/v2/me/supervision-disputes, POST /api/v2/me/supervision-release-requests Ayuda/corrección o emancipación; verificación adecuada y revisión, sin transferencia automática de control ni chat.

Evidencia y callbacks de tutela/edad son interfaces internas del adaptador elegido; no aceptan afirmaciones de estado del navegador. La revocación produce platform.access-changed, invalida suscripciones y vuelve a proyectar vistas propias; a los demás solo indisponibilidad genérica. La revisión del vínculo no sustituye safetyRevision ni viceversa. En una sala protegida la cancelación por seguridad usa el núcleo con su revisión actual, no una revisión aportada por el responsable sobre una partida a la que quizá no tiene acceso.

Moderación y publicación interna

Operación Contrato mínimo
GET /api/v2/me/restrictions Motivo notificable, alcance/vigencia y posibilidad de recurso; sin identidad del reportante ni reglas internas de detección.
POST /api/v2/me/appeals, GET /api/v2/me/appeals/{appealId} Restricción/caso propio, nota acotada, estado y decisión; identidad verificada aunque esté suspendida, sin exigir chat/SMS.
GET /internal/v2/moderation/cases, GET /internal/v2/moderation/cases/{caseId} MFA, rol, asignación y acceso auditado; paginación acotada, sin exportación masiva de evidencia por defecto.
POST /internal/v2/moderation/cases/{caseId}/decisions expectedRevision, acción, motivo, alcance y duración; recibo idempotente + restricción + auditoría + invalidaciones confirmados juntos.
POST /internal/v2/moderation/cases/{caseId}/reviews Revisión por otra persona, precondición de caso y resolución auditable; no sobreescribe la decisión previa.

Las interfaces internas no se hacen públicas por compartir un binario Go: ruta/red/roles separados y ningún acceso con cookie de jugador. Publicación de juegos se realiza solo por pipeline/consola interna del equipo con permiso de publicación; no existe POST /api/v2/games para usuarios. El identificador de build/artefacto debe corresponder a paquete validado y reglas disponibles, no a una URL ejecutable elegida por el solicitante.

Powered by TurnKey Linux.