Vista generada de dec-0131: Federación entre servidores: pares emparejados, concesiones acotadas que se canjean por SCT, contenido remoto como `SourceBinding` y Jellyfin como importación y sincronización
docs/decisions/dec-0131-federacion-entre-servidores.mdVista generada desde
docs/decisions/dec-0131-federacion-entre-servidores.md. No se edita a mano:bun run docs:genla regenera ybun run docs:checkfalla si difiere. El estado aquí es el del model: si discrepa con otra página, manda el model.
| Campo | Valor |
|---|---|
| Estado | PROPOSED |
| Fecha | 2026-10-01 |
| Fichero | docs/decisions/dec-0131-federacion-entre-servidores.md |
Por qué importa (del frontmatter del ADR):
Fija qué significa federar en Styx cuando hay dos servidores de dueños distintos (o dos instalaciones independientes del mismo dueño) y cómo se relaciona con Jellyfin: identidad de servidor por clave Ed25519 emparejada fuera de banda, peticiones servidor a servidor firmadas (RFC 9421), concesiones de biblioteca a un par acotadas, caducables, revocables y no recompartibles que viven en el servidor que concede, intercambio de cada concesión por SCT v1 de sesión sin cambiar su formato, contenido remoto como un SourceBinding más del catálogo federado de r08 (kind remote-styx) emparejado por huella de contenido e ids externos, feed de cambios por biblioteca con cursor, estado de usuario que no sale nunca de su servidor de origen, y la importación y sincronización con Jellyfin (usuarios como invitaciones, progreso, favoritos y bibliotecas) como relaciones del adaptador de dec-0035. Sin este ADR un executor mezclaría tres sentidos de "federado" (fuentes de r08, nodos de dec-0031 y servidores ajenos), crearía cuentas cruzadas o tokens largos que el daemon no sabe revocar, o abriría NATS entre servidores de dueños distintos.
Nodos del roadmap que lo citan en refs: ninguno.
Páginas de la documentación que lo citan: Servidores conectados y Jellyfin (especificado)
Leído de docs/decisions/dec-0131-federacion-entre-servidores.md, el fichero canónico.
SourceBinding y Jellyfin como importación y sincronizaciónAskUserQuestion, con las
preguntas de §15 resueltas o diferidas de forma explícita. Hasta entonces no se escribe código
de producción que dependa de él. El nodo outcome/server-federation es una alta propuesta queued en el model, pendiente de ratificar en
P1 (§15).outcome/server-federation gana los milestones own-nodes y peers. La federación queda fuera
de los alfas (excludes de alpha.1, dec-0133).w10/adr-federacion, desde w10/vision). Número
reservado para este workflow: dec-0127 modelo de datos, dec-0128 dispositivos, dec-0129
transferencias, dec-0130 modo público y plataforma, dec-0131 federación (este). Los cuatro
primeros se escriben en paralelo; este ADR los cita por número y no por contenido.docs/overview/vision-ledger.yml
V-N06): "la federacion me suena q tambien hablada pero me cuadra tu idea y la tomo, ya que
asi el poder tenr la misma en varios styx, o en styx y jellyfin, o poder sincronizar
bibliotecas importarlas etc". La "idea" que waxin toma es la propuesta que se le hizo en la
pregunta: compartir con amigos entre servidores con permisos acotados y caducables (SCT), sin
cuentas cruzadas. La cita del texto de esa opción no está en las transcripciones guardadas; la
formulación de N6 que da el workflow es la fuente.V-N06 (principal), V-PROD-07 (Jellyfin consumible,
sustituto y no fuente), V-ARCH-13 (nodos first-class: aquí se separa nodo de par). Toca sin
cerrar: V-N01 (metadatos en el fichero: §6.3), V-N07/V-N10 (descargas y offline de
contenido compartido: §7.4, lo cierra dec-0129), V-N08 (modo público: §2.3, lo cierra
dec-0130), V-UX-01 (el usuario "va a entender 0" de "federado": §2.1 y §11).r01: los bytes de vídeo no atraviesan JS ni NATS. Tampoco entre servidores.r04: SeekableMediaSource. Un servidor remoto es una fuente más (§6.2).r08: catálogo federado Work → Edition → MediaAsset → SourceBinding. Aquí "federado"
significa varias fuentes por asset dentro de un servidor; este ADR lo reutiliza, no lo
redefine (§2.1).r16: frontier salvo blocker (§4.4 explica por qué la concesión no es un token delegable).r17: un contenedor por authority. Este ADR no crea servicio nuevo (§8, P6).r25: Federated Media Runtime. Sus "nodos federados" son de un mismo dueño.r28: cada pieza termina en un consumidor real (§13).dec-0031/dec-0032: Styx Node first-class y NATS leaf hub-and-spoke del mismo dueño.
Este ADR no los toca y prohíbe extender NATS a un par (§3, regla 2).dec-0035: Jellyfin sustituto, no fuente, con cuatro relaciones (importador, SourceBinding
remoto, facade de API, exportador). Este ADR detalla 1, 2 y 4 para N6 (§9).dec-0090: handoff con autoridad única. Un handoff entre servidores queda fuera (§14).dec-0114: huella de contenido. Es la identidad que empareja assets entre servidores (§6.3).dec-0117 (LOCKED): SCT, fronteras B1/B2, I1/I2. protocols/capability-token/SCT_SPEC.md:
SCT v1 de 204 bytes, aud = nodo, sid abierto por playback-svc, vida ≤ 600 s.dec-0118 (LOCKED): BFF, CORS, audit. dec-0119/dec-0120: spire y BUS_ROUTES.dec-0121 (LOCKED): ingesta con conduit; la importación de bytes entre servidores la usa.dec-0124 (PROPOSED): la página explicacion/federacion (estado especificado).dec-0125 (PROPOSED): cuentas, hogares, concesiones de biblioteca (library_grants),
invitaciones, permiso efectivo por intersección. Este ADR añade un tipo de beneficiario
(§5.2).dec-0126 (PROPOSED): metadatos en el fichero; su §13 deja fuera "la federación de ficheros
con artwork", que cae aquí (§6.3).| Pieza | Estado | Dónde |
|---|---|---|
Catálogo con varias fuentes por asset (SourceBinding) | existe el modelo | packages/domain, r08 |
| SCT v1 de sesión: firma playback-svc, verifica el daemon | existe | protocols/capability-token/SCT_SPEC.md, spire.cap |
| Concesiones de biblioteca a cuenta u hogar | propuesto | dec-0125 §4.1, tabla library_grants |
| Huella de contenido | propuesta, 0 hits en código | dec-0114 §3, dec-0126 §1.2 |
| Metadatos en el fichero | propuesto | dec-0126 |
| Nodos del mismo dueño, NATS leaf | decidido, sin código (track/node-foundation queued) | dec-0031, dec-0032 |
| Integración Jellyfin | decidida en cuatro relaciones, sin código (integrations/jellyfin/ vacío salvo README) | dec-0035, outcome/jellyfin-compat queued |
| Servidor a servidor entre dueños distintos | no existe ni en papel | este ADR |
| Dueño del estado por perfil (progreso, vistos, favoritos) | sin dueño | dec-0125 §19 lo deja abierto |
Dos consecuencias:
outcome/federated-storage (que es storage, r26) o de track/node-foundation (que es un
solo dueño y un solo bus).| Término en este repo | Qué es | Confianza | ADR |
|---|---|---|---|
| Catálogo federado | Un asset con varias fuentes (local, Storage Box, torrent, remoto) | un servidor | r08 |
| Nodo | Proceso de un mismo despliegue en otra máquina (home, VPS, NAS) | un dueño, un bus NATS, un catálogo | dec-0031, dec-0032 |
| Par (peer) | Otro servidor Styx con su dueño, su identity, su catálogo y su bus | ninguna por defecto; la concesión | este ADR |
| Adaptador Jellyfin | Un servidor Jellyfin al que Styx habla por su API | la de una API key de Jellyfin | dec-0035 + este ADR §9 |
En la UI la palabra "federación" no aparece (V-UX-01). El usuario ve "Servidores
conectados" (pares), "Compartido contigo" (lo que otros le conceden) y "Importar de
Jellyfin". "Federación" queda como término de arquitectura y de la doc de explicación.
| # | Caso | Mecanismo |
|---|---|---|
| F1 | La misma biblioteca en varios Styx del mismo dueño, una sola biblioteca lógica | Nodos (dec-0031), no pares. Este ADR no lo cambia; sólo lo señala (§3, regla 1). |
| F2 | La misma biblioteca en varios Styx independientes (otro sitio, otra instalación) | Par con concesión de biblioteca completa y vínculo de cuenta opcional (§5, §7.3). |
| F3 | Compartir con amigos entre servidores, sin cuentas cruzadas | Par con concesión acotada y caducable, canjeada por SCT de sesión (§4, §5, §6). |
| F4 | Styx ↔ Jellyfin: importar usuarios, progreso y bibliotecas, sincronizar mientras convivan | Relaciones 1, 2 y 4 de dec-0035, detalladas en §9. |
Y una operación transversal: importar una biblioteca de un par (copiar catálogo y, opcionalmente, bytes) para dejar de depender de él (§7.5).
dec-0130. Un servidor público
puede además ser par; se decide allí.dec-0128. Entre servidores distintos, no (§14).dec-0129.dec-0031). Un par es otro servidor con su
propio catálogo y su propio dueño, aunque el dueño sea la misma persona (F2).dec-0032, BUS_ROUTES,
ACL por servicio). Entre pares sólo hay HTTP firmado (§4.3) y bytes por el data plane (§6.2).peerId = base32(sha256(clave pública)), al estilo
de los device IDs de Syncthing. Se empareja fuera de banda con un enlace o QR de un solo
uso (§4.2); no hay directorio central ni descubrimiento abierto.created, nonce y caché de replay, sobre HTTPS/HTTP3 (§4.3).actor = fed:<peerId>:<handle>. El
formato SCT no cambia y el daemon no aprende nada nuevo (§6.1).handle seudónimo y estable por concesión; el que concede audita, limita y
revoca por handle sin saber quién es (§5.3).SourceBinding más (kind remote-styx) en el catálogo del par
que recibe, emparejado con lo local por huella (dec-0114) e ids externos (dec-0126). Si
el amigo ya tiene la película, no se duplica: gana otra fuente (§6.3).SeekableMediaSource remota con SCT
(§6.2). Directo cliente → par como optimización (P3).dec-0126) y lo que la concesión declare (§7).dec-0125, progreso, favoritos,
bibliotecas), sincronización bidireccional de progreso mientras convivan, SourceBinding
remoto y exportador (§9). Jellyfin no es par: no firma, no concede, no recibe SCT.Cada servidor publica en GET /.well-known/styx/server (sin autenticar, cacheable):
{
"peerId": "b32:…",
"name": "Casa de waxin",
"keys": [{ "kid": "…", "alg": "ed25519", "pub": "…", "notAfter": "…" }],
"endpoints": { "fed": "https://styx.example/fed/v1" },
"protocol": { "fed": [1] },
"software": { "train": "…" }
}keys admite actual y siguiente (rotación, igual que STYX_SCT_PUBLIC_KEYS). La clave de
servidor es distinta de la de firma de SCT (A4 de dec-0117 no se reutiliza: separar
propósito).peerId (autocertificado, como las claves de
servidor de Matrix). Un cambio de clave sin firma de la anterior rompe el emparejamiento y
pide reemparejar (es la señal de compromiso o reinstalación).peer:manage) de A crea una invitación de par: enlace o QR con
peerId de A, endpoint, código de un solo uso (≥ 128 bits), caducidad (por defecto 24 h) y,
si se crea junto con una concesión, su id.peerId coincide con el hash de la clave y presenta el código en una petición firmada con su
clave.dec-0125 §7.3), registra B con su clave fijada y responde firmado.El amigo sin servidor propio no es un par: para él existe la invitación de cuenta de
dec-0125, que sí crea una cuenta en A. Este ADR no inventa un tercer camino.
@method,
@target-uri, content-digest (RFC 9530) cuando hay cuerpo, y los parámetros created,
keyid, nonce.created ± 30 s (la misma tolerancia que la SCT), caché de nonce acotada y
pre-reservada que rechaza al llenarse (el patrón ReplayCacheFull de la SCT).fed/v1 sólo existe si STYX_FEDERATION=enabled (apagado por defecto), escucha
detrás del mismo borde que el resto de la API (dec-0118), con rate limit por peerId y
403 genérico sin enumerar (como H3 en la SCT).GET al host de la invitación, sin seguir redirecciones a
otro host, con resolución que excluye rangos privados salvo STYX_FEDERATION_ALLOW_PRIVATE
(pares en LAN) y con tamaño y plazo acotados.Biscuit (bloques Ed25519 con Datalog, atenuación offline) y UCAN 1.0 (cadenas de delegación con DID) son el estado del arte de capabilities delegables. Se estudiaron y no se adoptan en v1:
spire.cap en Zig. Ni Biscuit ni UCAN tienen implementación Zig, y meter Datalog o DAG-CBOR
en el daemon contradice dec-0117 (parsers mínimos, fuzz).dec-0117): no hay camino
offline que ganar.Queda como evolución (P4): si un día un par tiene que operar sin conexión con el que concede
(descargas offline de contenido compartido, dec-0129), la concesión podría emitirse además
como Biscuit atenuable verificado sólo en TS. No cambia el daemon.
| Campo | Contenido |
|---|---|
grantId | id opaco |
peerId | par beneficiario |
principals | any (cualquier usuario del par que su dueño autorice) o lista de handle (§5.3) |
scope | bibliotecas, colecciones o works concretos |
permissions | subconjunto de browse, play, download, import (§7.5) |
constraints | calidad o bitrate máximos, transcodificación permitida o no, streams simultáneos por concesión y por handle, ancho de banda, bytes/mes |
maturity | techo de clasificación (el del par se interseca, nunca se amplía) |
expiresAt | obligatoria. Máximo configurable (STYX_FEDERATION_GRANT_MAX_TTL, P2) |
reshare | siempre false. No es un campo configurable; está para que el contrato lo diga |
createdBy, createdAt, revokedAt, revokedBy | audit |
dec-0125library_grants con grantee_kind = peer y una tabla
hija peer_grants con los campos de §5.1. Se propone como enmienda a dec-0125 §4.3, que
entra con el lock de los dos.peer:grant (owner o admin) y que quien concede tenga
a su vez esas bibliotecas (dec-0125 §5.3: nadie concede lo que no tiene), con auth_time
reciente.handle remoto en A es concesión ∩ restricciones de A, y en B es
además ∩ lo que el dueño de B permita a ese usuario (B no puede ampliar lo que A dio).handlehandle = base32(HMAC-SHA256(clave por concesión de B, accountId ‖ profileId))[0..16].handle) y no
correlacionable entre concesiones ni entre pares.displayHint voluntario.handle.evt.federation.grantRevoked en el bus de A y A
cierra por IPC las sesiones abiertas con actor = fed:<peerId>:* de esa concesión. La SCT ya
garantiza que cerrar la sesión corta MoQT y H3/WT en la siguiente petición (SCT_SPEC,
"Alcance de la revocación").SourceBinding remote-styx de esa concesión como no
disponibles; no borra Works ni estado de usuario.cliente del amigo ──(BFF de B)──▶ playback-svc B
│ POST fed/v1/sessions (firmada RFC 9421)
│ { grantId, handle, assetRef, plan }
▼
fed/v1 de A ──▶ identity A (concesión) ──▶ playback-svc A
│ abre sesión por IPC (I2)
│ SCT v1: aud=nodo A,
│ actor=fed:<peerId>:<handle>,
│ resource=asset, scope=read
◀──────────── { sessionId, sct, endpoints, exp } (firmada)actor_id = "fed:" ‖ peerId ‖ ":" ‖ handle; el digest de actor de la SCT ya separa dominios,
así que un actor federado nunca colisiona con una cuenta local.assetRef es un id opaco por par (A no expone sus assetId internos ni {rootId, relPath}, dec-0117 I7). Evita correlación entre pares.dec-0129 y la renovación de SEC-Z13 lo resuelven para los dos
casos a la vez.RemoteStyxSource
(SeekableMediaSource, r04) que lee de A por HTTP/3 con rangos y la SCT. El cliente del
amigo sólo habla con B: misma sesión, mismo CSP, mismo CORS, mismo progreso. B puede servir
desde su caché (r12) a varios usuarios suyos dentro de la concurrencia que A concede y
puede remuxar o empaquetar con su byte runtime. Los bytes nunca pasan por JS (r01).dec-0118) y expone la IP del amigo a A.delivery-edge o el VPS
hub de dec-0032). Es la misma restricción que tiene hoy el acceso remoto de cualquier
cliente; este ADR no la resuelve (P7).IMDB, TMDB, TVDB2), huella de contenido (dec-0114, sobre payload según la
enmienda de dec-0126), facts técnicos y artwork por hash.MediaAsset, se añade un SourceBinding remote-styx;
mismos ids externos y otra huella → mismo Work/Edition, otro MediaAsset (otra
calidad); nada → Work nuevo marcado como "de ". El selector de fuentes de r08 ya
sabe elegir entre local y remota.dec-0126) hacen la proyección reconstruible: lo que A
manda es lo mismo que va dentro del fichero, y si B importa los bytes (§7.5) el fichero ya
lleva su metadato. El artwork se sirve por hash de contenido y B lo cachea como derivado (el
modelo de dec-0127).GET fed/v1/libraries/:grantScope/changes?cursor=… devuelve eventos ordenados
(assetAdded, assetRemoved, metadataChanged{metaHash}, artworkChanged{hash}) y un cursor
nuevo. Patrón de Syncthing (índice con versiones) y del /sync de Matrix, sin su complejidad:
aquí el flujo es unidireccional (A publica, B consume).metaHash es el hash de metadatos que N1 pide guardar en la base para detectar cambios
barato: B sólo pide el detalle de lo que cambió.En F3 (amigos) el progreso del amigo vive en B. A no lo ve ni lo guarda. A sólo guarda contadores
de uso por handle para topes y audit, con retención acotada.
Si la misma persona tiene cuenta en A y en B (dos instalaciones independientes), puede
vincularlas con su consentimiento en los dos lados (aprobación con auth_time reciente en
ambos). El vínculo permite replicar su estado por perfil (progreso, vistos, favoritos):
(perfil, itemKey) con reloj lógico híbrido (HLC); itemKey = huella si hay,
si no ids externos + temporada/episodio;Depende de que el estado por perfil tenga dueño (P5).
download en la concesión permite pedir el fichero (o una variante generada al vuelo, N7) para
reproducir sin conexión. El mecanismo (conduit, reanudación, generación al vuelo, caducidad
offline) es dec-0129. Lo que fija este ADR: una descarga de contenido compartido cuenta contra
los topes de la concesión y su caducidad offline no supera expiresAt.
import en la concesión permite a B copiar contenido de A para dejar de depender de él:
dec-0121) hacia la raíz de ingesta de B, reanudable, con la misma SCT de
scope read en el lado de A y scope ingest en el lado de B;Importar es la forma de mover una biblioteca entre instalaciones (F2) y de "llevarse" lo que un
amigo concede con permiso explícito. Sin import, nada se copia de forma persistente salvo la
caché derivada de B, que se purga al revocar.
| Pieza | Dueño |
|---|---|
Pares, claves fijadas, invitaciones de par, concesiones, handle | identity-svc (authority de credenciales y concesiones, dec-0087, dec-0125) |
Endpoint fed/v1 (verificación RFC 9421, rate limit, replay) | borde HTTP de la API (el gateway de W2 que cierra el ADR headless); librería pura @styx/federation |
| Proyección de catálogo y feed de cambios | catalog-svc |
| Sesiones para actores federados, SCT | playback-svc |
RemoteStyxSource | daemon (byte runtime), detrás de SeekableMediaSource |
| Adaptador Jellyfin | integrations/jellyfin/ sobre el plugin-sdk (dec-0033, r48), dentro de outcome/jellyfin-compat |
No se crea federation-svc (P6): no hay authority nueva, sólo un tipo de beneficiario y un
endpoint.
dec-0035 fija cuatro relaciones. N6 pide "la misma en varios styx, o en styx y jellyfin, o
poder sincronizar bibliotecas importarlas". Se detallan así:
| Qué | Cómo |
|---|---|
| Bibliotecas | Cada biblioteca de Jellyfin propone una raíz y una biblioteca de Styx. Las rutas se mapean si el daemon ve el mismo disco; si no, se ofrece SourceBinding remoto (9.2) o importar bytes por conduit. |
| Items | Emparejados por huella cuando el fichero es accesible, y por ids de proveedor (ProviderIds) y ruta como apoyo, como hacen las herramientas de sincronización de vistos entre Jellyfin, Plex y Emby. |
| Metadatos y artwork | NFO y artwork de Jellyfin como importación (dec-0126): pasan por el plugin de enriquecer/normalizar y, si la raíz lo permite, se escriben en el fichero (N1). Jellyfin no es fuente de verdad. |
| Usuarios | Cada usuario de Jellyfin propone una invitación de dec-0125 con sus bibliotecas ya concedidas. No se migran passwords (Jellyfin usa password local; Styx es passwordless por defecto). |
| Progreso, vistos, favoritos | Desde UserData de Jellyfin (PlaybackPositionTicks, Played, PlayCount, IsFavorite, LastPlayedDate), aplicado al perfil de la cuenta que canjee la invitación. |
| Colecciones y listas | Como colecciones de Styx de la cuenta que las importa. |
El importador es idempotente y reanudable (cursor por biblioteca y por usuario) y deja un informe: emparejados, ambiguos (a revisión), no encontrados.
SourceBinding remoto y sincronización (relación 2)RemoteJellyfinSource y sincronizar progreso en los dos sentidos con una
API key de Jellyfin y un mapeo de usuarios.LastPlayedDate) frente al HLC de Styx; "visto" monótono
salvo desmarcado; nunca se escribe en Jellyfin nada que no sea UserData (sin metadatos, sin
borrados).Con N1 el exportador es casi gratis: el fichero ya lleva sus metadatos y su portada. Exportar añade, si se pide, NFO y artwork al lado para Jellyfin, y el progreso por usuario a su API.
No es par. No tiene identidad de servidor, no firma, no concede ni canjea SCT. La facade de API (relación 3, Compatibility Profiles) es otra cosa: clientes de Jellyfin hablando con Styx.
| # | Alternativa | Por qué no |
|---|---|---|
| A1 | Cuentas cruzadas (el amigo tiene cuenta en cada servidor) | Es lo que N6 descarta. Además duplica passkeys, invitaciones y estado por servidor. |
| A2 | Directorio central (modelo plex.tv) | Plex comparte libraries a cuentas de plex.tv: un tercero ve quién comparte con quién y su caída corta el acceso. Contradice "owned e2e". |
| A3 | NATS entre servidores (leaf o gateway hacia el par) | Mezcla buses de dueños distintos, rompe BUS_ROUTES y las ACL por servicio, y saca el bus de su dominio de confianza (dec-0032). |
| A4 | Concesión como token portador largo (JWT, Biscuit, UCAN) | Revocación difícil, recompartir posible, verificador nuevo en el daemon. §4.4. Biscuit queda como evolución sólo en TS (P4). |
| A5 | ActivityPub (seguir servidores como PeerTube) | Pensado para difusión pública; su modelo es "seguir y recibir todo", no conceder con topes y caducidad. Sus firmas HTTP aún no han migrado a RFC 9421. Útil como referencia para modo público (dec-0130). |
| A6 | mTLS con certificados fijados (Syncthing) | Buena identidad, mala con proxies inversos y bordes que terminan TLS (Coolify, delivery-edge). Se toma su peerId = hash(clave), no el transporte. |
| A7 | Replicar bytes siempre (redundancia de PeerTube) | Para F3 es copiar la biblioteca de un amigo sin su permiso de import. Queda como opción explícita (§7.5), no como comportamiento por defecto. |
Referencias consultadas: RFC 9421 (HTTP Message Signatures) y su adopción en el fediverso;
especificación de Biscuit (Ed25519, atenuación offline con Datalog); UCAN 1.0-rc (delegación e
invocación); ayuda de Plex sobre compartir servidores (sin recompartir, cuentas de plex.tv);
redundancia de PeerTube; JellyPlex-Watched y WatchState (sincronización de vistos entre Jellyfin,
Plex y Emby por ids de proveedor y nombre de fichero); DTO UserItemDataDto de la API de
Jellyfin; device IDs de Syncthing. De ninguno se toma código: son referencias de diseño
(regla sin GPL; la licencia de cada uno se comprueba si algún día se quisiera reutilizar algo).
protocols/federation/ nuevo: spec humana de fed/v1 (documento de servidor, emparejamiento,
sesiones, feed, notificaciones, errores), schemaVersion, política de compatibilidad y
vectors.json con firmas RFC 9421 válidas y alteradas (regla de protocols/CLAUDE.md).@styx/api-contracts: PeerSchema, PeerInvitationSchema, PeerGrantSchema,
FederatedSessionRequest/ReplySchema, CatalogChangeSchema, JellyfinImportPlanSchema.BUS_ROUTES, por spire): evt.federation.peerPaired, .peerUnpaired, .grantCreated,
.grantRevoked; qry.identity.peerGrant; cmd.catalog.applyRemoteChanges.dec-0124 §6.1) con su CLI: styx peer invite|pair|list|unpair,
styx share grant|revoke|list, styx import jellyfin --plan|--apply.FEDERATION_DISABLED, PEER_UNKNOWN, PEER_KEY_MISMATCH, SIGNATURE_INVALID,
GRANT_EXPIRED, GRANT_REVOKED, GRANT_SCOPE_DENIED, GRANT_LIMIT_REACHED.| Amenaza | Mitigación |
|---|---|
| Par comprometido o malicioso | Topes por concesión además de por handle (§5.3); revocación inmediata; audit por peerId. |
| Recompartir | reshare no existe; assetRef opaco por par; relay por B sin entregar la SCT al cliente por defecto. |
| Enumeración de catálogo ajeno | fed/v1 sólo responde a pares fijados; 403 genérico; el feed sólo cubre el scope concedido. |
Agotamiento de A (A6 de dec-0117) | concurrencia, ancho de banda y bytes/mes por concesión; rate limit por peerId; topes del daemon por actor. |
| Replay de peticiones S2S | created ± 30 s, nonce en caché acotada que rechaza al llenarse. |
| Robo de la clave de servidor | rotación con firma de la anterior; sin firma, reemparejar; la clave vive fuera de la de SCT. |
| SSRF al emparejar | §4.3. |
| Privacidad del amigo | handle HMAC por concesión; relay por defecto (A no ve su IP); A no guarda su estado. |
| Fugas de rutas o ids internos | assetRef opaco; nunca {rootId, relPath}; telemetría sin handle en claro (A8). |
| Jellyfin: API key con más poder del necesario | se pide una key de un usuario admin sólo para importar; para la sincronización, tokens por usuario; nunca se escriben metadatos en Jellyfin. |
| Pieza | Consumidor |
|---|---|
| Importador Jellyfin | waxin migrando su biblioteca de Jellyfin del Storage Box (V-PLAY-03) con sus usuarios y vistos |
| Sincronización con Jellyfin | la convivencia durante esa migración |
| Pares + concesiones + canje por SCT | compartir con un amigo que tenga su propio Styx (F3) |
| Feed de cambios | la biblioteca "Compartido contigo" de B |
| Vínculo de cuenta + LWW de progreso | F2: dos instalaciones de waxin (p. ej. casa y otra ubicación) sin unirlas como nodos |
| Importar de un par | mover una biblioteca entre instalaciones |
Si una pieza no tiene consumidor cuando toque implementarla, no se implementa. El orden recomendado es Jellyfin primero (consumidor inmediato), pares después.
dec-0090 es intra-servidor; dec-0128).dec-0125 §19 también deja abierto.styx.model.yml y su gate (P1).outcome/jellyfin-compat como está, y un nodo nuevo outcome/server-federation (queued) con
dependsOn: [track/identity, outcome/first-vertical] para pares y concesiones. Alternativa:
colgarlo todo de outcome/jellyfin-compat. El nodo recomendado ya es alta propuesta queued en
el model, sin gate, pendiente de tu OK; si eliges la alternativa, se borra.dec-0125 §19).federation-svc. Recomendado: identity + catalog + playback + librería
@styx/federation, sin servicio nuevo. O un servicio dedicado si el endpoint fed/v1 crece.SourceBinding (§6.3); que el estado de usuario no sale
de su servidor salvo vínculo explícito (§7.2–7.3); que Jellyfin no es par (§9.4).library_grants gana un
tipo de beneficiario (enmienda propuesta a dec-0125).remote-styx de SourceBinding.fed:*; el daemon gana RemoteStyxSource pero no
cambia su verificación de SCT.integrations/jellyfin/ deja de ser un README cuando haya consumidor (la migración de waxin).explicacion/federacion (estado especificado) se ata a este ADR (dec-0124).V-N06 a este ADR; el GAP sigue abierto hasta el lock y el
nodo de P1.dec-0035 recibirá un banner "detallado por dec-0131" en el lock, no antes.dec-0125 §4.3 y §5.2 reciben la enmienda de §5.2 en el lock de ambos.dec-0031, dec-0032, dec-0114, dec-0117, dec-0121,
dec-0126, y los reservados dec-0127, dec-0128, dec-0129, dec-0130.dec-0130
Vista generada de dec-0130: Modo público y Styx como plataforma: registro abierto o con aprobación, roles atados al registro, entrega por biblioteca, moderación y retirada, superficie pública publicable y frontend plantilla
dec-0132
Vista generada de dec-0132: Styx multi-tipo: asset genérico, plugins de tipo de contenido, acciones y pipelines por kind