Vista generada de dec-0129: Transferencias como columna vertebral: subidas, descargas, offline, p2p y adquisición en un solo plano
docs/decisions/dec-0129-transferencias-columna-vertebral.mdVista generada desde
docs/decisions/dec-0129-transferencias-columna-vertebral.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 | LOCKED |
| Fecha | 2026-10-01 |
| Fichero | docs/decisions/dec-0129-transferencias-columna-vertebral.md |
Enmienda a: r26, dec-0117, dec-0121
Por qué importa (del frontmatter del ADR):
Fija que subidas, descargas, offline, p2p/swarm y adquisición son un solo plano de transferencias del byte runtime, con un modelo de transferencia reanudable común, identidad verificable por rangos (BLAKE3 en árbol con grupos de 16 KiB y outboard de bao; el árbol SHA-256 de BitTorrent v2 se calcula bajo demanda sólo para sembrar) e indexado inmediato de todo lo que entra. Hace de la descarga un ciudadano de primera clase igual que el stream: original byte a byte con Range/If-Range, o derivada (remux determinista con tamaño exacto, o transcodificación materializada por ExtentMap en la caché direccionada por hash) en el formato o códec pedido, con los metadatos incrustados. Abre el scope SCT download y el permiso asset:download, añade clases al GlobalByteScheduler para que una descarga nunca degrade una reproducción, y fija objetivos de rendimiento medibles contra referentes (nginx sendfile+kTLS, aria2, rsync, qBittorrent). Sin este ADR cada dirección inventaría su propio modelo de reanudación, de integridad y de cuota, y la descarga quedaría como un GET suelto con un token de reproducción de 10 minutos.
Nodos del roadmap que lo citan en refs: track/byte-runtime/egress, track/byte-runtime/ingest-web, track/byte-runtime/dedup, outcome/federated-storage/remote-backends, track/media-engine/download-output
Páginas de la documentación que lo citan: Transferencias (especificado)
Leído de docs/decisions/dec-0129-transferencias-columna-vertebral.md, el fichero canónico.
AskUserQuestion: lockear este ADR con la
enmienda de P3 que propone docs/track/byte-runtime/plans/conduit-wire-v2.md (rama
w13/conduit-v2): BLAKE3 + bao con hojas de 16 KiB como identidad interna, y el árbol SHA-256
de BEP 52 calculado bajo demanda, sólo para sembrar. Añade que el dominio de dedup es
configurable (household por defecto; user y server, este último con aviso). El resto de
§12 se lockea con la recomendación del propio ADR como parámetros reversibles, o queda diferido
de forma explícita. Todo está en Lock (2026-10-02, waxin). Donde el
texto de §1–§13 y el lock difieren, gana el lock. Las enmiendas de §11 entran en vigor hoy.w10/adr-transferencias, desde w10/vision). La
implementación cae en track/byte-runtime, track/media-engine, track/offline-continuity,
track/swarm-core, track/acquisition-core, track/identity y los clientes. Este ADR no crea
nodos ni milestones: los propone como pregunta (§12, P1).dec-0132 (LOCKED 2026-10-02, §9 y su tabla de
relación con transferencias) propone que el origin de §3.7 gane device (volcado desde una
consola) y que la ingesta agrupe una carpeta como N transferencias más un manifiesto, con
bundleId y manifestRoot en @styx/api-contracts ingest.ts. Ningún lock la recoge en este
ADR: hasta que waxin la lockee (AskUserQuestion), §3.7 y el contrato de ingesta no cambian.dec-0127 (modelo de datos), dec-0128 (dispositivos), dec-0130 (modo
público y plataforma) y dec-0131 (federación) están reservados para el mismo workflow. Se
citan por número; su contenido no se presupone.docs/overview/vision-ledger.yml):
V-N07: "y ojo con las descargas . que eso no es una idea suelta, eso e sLOAD BEARING ,
todo lo d descargas, subidas, p2p etc , y mas siporte que haremos , es una columna vertebrak
y debe ser ultrarapida y state of the art."V-XFER-01: "jelyfin nisiquiera ofrece para subir un fichero directaenre y es un coñazo tener
que tirar rsync o ftp o lo q sea y liego esperar el refresh o relwanzar el scan, nosotros
hacemos todo eso y ademas con conduit ofrecemos descargas y subidas a prueba de cortes,
reanudables, ultrarrapidas ."V-XFER-02: "y ls descargas igual de importantes que el stream, lo mismo. byteruntime si hace
falta si solicita descargarlo con algun format. o codec que actualmente no esta , se hace on
the fly como el stream. muy importantes las descargas."V-XFER-03: "no solo adquisicion de contenido de torrents o demas como jellyfin plugins para
utomatizarlo".V-N10: "Federación + offline" (offline y descargas reanudables, ligado a N7).V-XFER-04 (BitTorrent propio), V-XFER-05 (Acquisition Runtime,
dec-0034) y toca V-N03 (backend remoto), V-N06 (federación) y V-N08 (modo público:
"stream, descarga o ambos").r01: Bun decide, Zig ejecuta; los bytes no atraviesan JS ni NATS.r04 / C14: SeekableMediaSource, cancel(requestId) por request.r05: PlaybackPlan composicional; este ADR añade la entrega file.r12 / r45: caché de 3 tiers; L2 desaloja por coste de regeneración.r16: frontier salvo blocker.r23 / dec-0019: CanonicalCodecPolicy, AcquisitionArtifact, seeding lease.r26: byte runtime en todas las direcciones, ReadIntent, GlobalByteScheduler,
multi-source reads, crate swarm.r27 / dec-0034: nodos, Acquisition Runtime, varilla SOTA contra qBittorrent.r61 / dec-0094 / dec-0098: OfflineBundle, track/offline-continuity.dec-0106 (PROPOSED): ExtentMap con known_length y sealed.dec-0110: MediaEngine dual; §1.2 audio a AAC; el encode de vídeo no está decidido.dec-0114 / dec-0126 (PROPOSED): huella sobre el payload; metadatos dentro del fichero.dec-0117: I1–I12, SCT v1 y sus scopes.dec-0119 / dec-0120: control y bus por spire, BUS_ROUTES.dec-0121: ingesta con conduit, SCT ingest, cuotas, cmd.catalog.registerIngested.dec-0124 (PROPOSED): doc como contrato y superficie headless.dec-0125 (PROPOSED): permisos <recurso>:<acción> y concesiones de biblioteca.Verificado sobre el árbol de w10/vision.
| Dirección | Estado |
|---|---|
| Subida | Existe y está cerrada (track/byte-runtime/ingest done, IN1–IN3 pass). styx-upload (Zig) → IngestSink del daemon con el wire v1 de conduit, SCT ingest, cuotas por fichero, actor y raíz, SHA-256 por chunk y del fichero, staging fd-relativo, alta en catálogo por cmd.catalog.registerIngested (dec-0121). No hay cliente web ni de la app: sólo el CLI. |
| Descarga | No existe como tal. El daemon sirve byte-ranges por H3/WT con una SCT read que dura 600 s, y la entrega se cierra a los 630 s (plan vertical-vod-web, rama w8/vertical-plan, fila "Sesión / entrega"). No hay permiso asset:download (packages/authz/src/policy.ts:20 sólo tiene play y scan), ni Content-Disposition, ni descarga en otro formato. |
| Formato al vuelo | Para reproducir: remux a fMP4/CMAF y HLS (native/zig/transmux/), passthrough de h264 hevc av1 vp9 aac mp3 opus flac ac3 eac3. Transcodificación de audio a AAC permitida por dec-0110 §1.2 y sin implementar. El encode de vídeo no está decidido (dec-0110, "Lo que este ADR NO decide"). |
| Wire de conduit | Sólo sube: cinco rutas (session, chunk, status, complete, abort; MKS2508/conduit spec/wire-v1.md §4). La frase de waxin "con conduit ofrecemos descargas" no tiene todavía soporte en el wire ni en el SDK. |
| Offline | OfflineBundle existe como tipo en r61 D1 (dec-0094). track/offline-continuity está queued sin consumidor real, como excepción nominada (dec-0098). |
| P2P / swarm | track/swarm-core y track/swarm-progressive queued. El crate swarm (BitTorrent v1/v2/hybrid) está en r26 §4 como plan. 0 líneas de código. |
| Adquisición | track/acquisition-core y track/acquisition-native queued (dec-0034). 0 líneas de código. |
| Rendimiento medido | No hay línea base honesta del daemon. Los ~20 MB/s de H3 medidos en la ronda 3 del motor eran el techo del cliente aioquic, no del daemon (docs/track/byte-runtime/evidence/media-engine-2026-09-28/hardening-r4/README.md, fila "Evidencia de throughput honesta"). La subida no tiene medida de goodput. Ninguna cifra de este ADR se puede afirmar hoy: todas son objetivos. |
Lo que hacen los demás, y qué se toma. Fuentes en §13.
| Referente | Qué hace | Qué se toma / qué no |
|---|---|---|
| Jellyfin | No sube ficheros (rsync/FTP + re-scan). Descarga el original; las descargas convertidas para offline no son de primera clase. | Es la vara: subir e indexar sin re-scan, y descargar en el formato pedido, son superset directo. |
| Plex | Descargas convertidas a una calidad menor en el servidor, para llevar al móvil. | La idea de perfil de descarga. No su modelo: aquí el derivado se cachea direccionado por hash y se reutiliza entre usuarios y entre stream y descarga. |
| HTTP Range / If-Range / ETag fuerte (RFC 9110) | Reanudar una descarga en cualquier cliente (navegador, VLC, curl, aria2) sin protocolo propio. | Es el wire de la descarga. Nada propio en el cable: cualquier gestor de descargas reanuda. |
IETF Resumable Uploads (draft-ietf-httpbis-resumable-upload-12, julio 2026, Standards Track, aún no RFC) | Subida reanudable estándar sobre HTTP (sucesor de tus). | Se sigue. conduit v1 sigue siendo el wire de subida (está hecho, verificado y es más estricto: digest por chunk, cuotas por identidad). Un adaptador al draft se decide cuando sea RFC (P8). |
| aria2 / gestores multi-conexión | Varias conexiones y rangos en paralelo para saturar enlaces con BDP alto o por-flujo limitados. | El cliente de descarga de conduit pide rangos en paralelo (§3.4.4). |
| BitTorrent v2 (BEP 52) y web seeds (BEP 19) | Árbol Merkle SHA-256 por fichero con bloques de 16 KiB: cada bloque se verifica al llegar; la raíz por fichero deduplica entre torrents. Web seeds: HTTP como un peer más. | La identidad verificable de §3.2 es ese árbol. Un asset de Styx es sembrable como v2 sin rehashear, y un rango de cualquier fuente se verifica por bloque. |
| iroh-blobs / BLAKE3 + bao | Streaming verificado por rangos sobre QUIC, con BLAKE3 como hash de árbol. | El modelo (rangos verificables de fuentes no confiables). BLAKE3 se evalúa frente al árbol de BEP 52 (P3): es más rápido, pero rompe la interoperabilidad directa con el swarm. |
| Background Fetch + OPFS | Descarga gestionada por el navegador que sobrevive a cerrar la pestaña (Chrome y Edge; no Firefox ni Safari). OPFS guarda ficheros grandes y sirve rangos sin leer desde el principio. | La descarga offline de la web (§3.5). Fallback en pestaña donde no hay Background Fetch. |
| "QUIC is not Quick Enough over Fast Internet" (2024) | Por encima de ~500–600 Mbit/s, QUIC/HTTP3 en navegadores entrega hasta un 45 % menos que TCP+TLS+HTTP/2, por coste de recepción (ACKs en espacio de usuario). | La descarga de fichero completo necesita un camino TCP, no sólo H3. Se mide (T1) y la elección es por medida, no por dogma. Choca con que no hay ADR del delivery-edge TCP (ledger V-PLAY-07). |
kTLS + sendfile (nginx ≥ 1.21.4) | Cifrado TLS en el kernel y cero copias de fichero a socket: 8–29 % más rendimiento según el SO, ~30 % en ficheros de 10 MB. | Es el referente del original (T1). La vara de la descarga del original es "igual que nginx sirviendo el mismo fichero en la misma máquina". |
Enmendado en el lock (P3). Las filas de BEP 52 y de iroh-blobs/BLAKE3 se leen al revés: la identidad interna es BLAKE3 + bao (§3.2) y el árbol de BEP 52 se calcula bajo demanda para sembrar.
Las transferencias son un solo plano del byte runtime (r26 §1: "owners completos del
flujo de bytes en todas las direcciones"), no cuatro features sueltas.
| Dirección | Entra / sale | Productor de bytes (Zig) | Decide (Bun) |
|---|---|---|---|
| Subida | cliente → nodo | IngestSink (conduit servidor) | playback-svc (dec-0121) |
| Descarga | nodo → cliente | egress del daemon: fichero original o derivado | playback-svc |
| P2P / nodos | peer ↔ nodo, nodo ↔ nodo | crate swarm (BitTorrent) y rangos verificados nodo↔nodo | acquisition-svc / federación (dec-0131) |
| Adquisición | fuente externa → nodo (torrent, HTTP, Jellyfin, otro Styx, backend remoto) | swarm, HTTP source, backends de outcome/federated-storage | acquisition-svc (dec-0034) |
Todas comparten:
Transfer { id, direction, content (§3.2), spec, progress (ExtentMap de lo hecho), actor, quota class, scheduler class (§3.8) }. La reanudación
siempre es "pedir lo que falta según el mapa de extents", nunca "empezar otra vez".cmd.catalog.registerIngested generalizado, §3.7). Nada se indexa
por re-scan.GlobalByteScheduler de r26 §2, con clases nuevas (§3.8).r01). Bun firma capabilities, decide políticas y ve progreso; nunca
ve un byte de vídeo.Texto del lock (enmienda de P3,
docs/track/byte-runtime/plans/conduit-wire-v2.md§2.1 en la ramaw13/conduit-v2). Sustituye a la propuesta original de un árbol SHA-256 de BEP 52 como identidad.
La raíz de transferencia de un fichero es BLAKE3(bytes exactos) — lo que imprime
b3sum —, que ya es un árbol de Merkle. La verificación por rangos va a grupos de 16 KiB
con un outboard pre-order (pares de CVs de los nodos padre sobre los grupos; 64 B por
16 KiB, 0,39 %), el layout de bao/iroh-blobs, definido en conduit spec/wire-v2.md §3 con
vectores. Rangos de una fuente no confiable se verifican en streaming (slices), grupo a
grupo, antes de escribirse o servirse, con memoria O(log n).
El outboard se guarda como caché regenerable en L2 de r45, direccionada por la raíz. Se
calcula en el mismo y único pase de la ingesta (conduit v2: el servidor hashea cada chunk
a su offset, guarda los CVs de grupo y deriva el outboard sin releer el fichero) o del escaneo.
BEP 52 bajo demanda: sus hojas (SHA-256 de 16 KiB) son los mismos rangos que los grupos. Al sembrar o contrastar un torrent v2, un pase verifica cada grupo contra el outboard BLAKE3 y calcula el SHA-256 en la misma lectura; el resultado (32 B por 16 KiB) se guarda como segundo outboard. Un torrent v2 que entra por adquisición se verifica con su propio árbol al bajar y recibe su outboard BLAKE3 en el pase que lo escribe.
Para qué sirve:
pieces root de BEP 52 sale de ese pase, verificado contra el outboard BLAKE3);Tres identidades, tres trabajos (y no se confunden):
| Identidad | Sobre qué | Cambia al incrustar metadatos | Para qué |
|---|---|---|---|
Huella de asset (dec-0126 §3.3) | payload (mdat / Clusters) + facts en lista blanca | no | "es la misma película": identidad en el catálogo |
| Raíz de transferencia | los bytes exactos del fichero, BLAKE3 | sí | integridad por rangos, swarm, dedup de bytes |
| ETag fuerte de la descarga | raíz de transferencia (+ perfil, si es derivado) | sí | If-Range: reanudar sólo si los bytes no cambiaron |
Con la enmienda, BLAKE3 es la única familia de hash de identidad del sistema (la huella de
asset de dec-0126 §3.3 y AssetId/metadataHash de dec-0127 ya lo son). SHA-256 queda
para interoperar (BEP 52, el perfil compat de conduit con cloudreve-mirror) y para lo que ya
firma con él: las SCT no cambian.
Consecuencia con dec-0126: una escritura de metadatos in-place cambia la raíz de transferencia.
El daemon no edita un fichero con una transferencia activa (descarga, siembra o subida a
otro nodo): la transferencia toma un lease, como el de seeding de dec-0019, y la edición
espera o se reprograma. Si la edición ocurre entre dos intentos, el If-Range falla y la
descarga recibe un 200 completo, que es el comportamiento correcto de HTTP.
Se mantiene dec-0121 entero. Lo que añade este ADR:
@mks2508/chunk-engine) y el SDK Zig compila a WASM. La web sube con ese cliente
desde un worker: el navegador lee el File por rodajas, calcula el CV BLAKE3 de cada
chunk (un pase) con el WASM del SDK de conduit, puede enviar los chunks en cualquier orden y
reanuda con el bitmap stored de v2 tras un corte o al reabrir la pestaña (el estado
local mínimo, uploadId + tamaño + mtime + nombre, vive en IndexedDB; los bytes los vuelve a
leer del fichero que el usuario re-elige). La app Swift usa el C-ABI del SDK. No hay un
segundo protocolo de subida.dec-0121 §3: una sesión, un
fichero), con concurrencia acotada por la cuota de sesiones por actor.complete → registerIngested → el asset está en el catálogo y es
reproducible y descargable sin re-scan. Después, en segundo plano y con la clase
background del scheduler: identificación por contenido (dec-0114), enriquecimiento y
metadatos al fichero (dec-0126, que dec-0127 revisa hacia fichero-SSOT, N1), y la
proyección del índice (dec-0127). El objetivo es medible (T4).outcome/federated-storage, V-N03) es una transferencia más del mismo modelo,
nodo → backend, con la clase background.Authorizer del
IngestSink da con la SCT ingest (configurable, household por defecto: lock, punto 3), sin
ninguna señal entre dominios ni descuento de cuota (oráculo de confirmación de ficheros).Una descarga se pide con un DownloadProfile, que se resuelve igual que un stream: el
planner (r05) produce un plan composicional con una entrega nueva, delivery: file.
| Perfil | Qué produce | Coste |
|---|---|---|
original | el fichero byte a byte | lectura |
remux | otro contenedor (MKV → MP4 con moov delante, o al revés), selección de pistas, subtítulos incrustados o aparte | lectura + muxer Zig |
transcode | otro códec, resolución o bitrate (audio por dec-0110 §1.2; vídeo cuando su ADR lo permita, §3.4.3) | CPU/GPU, se cachea |
+metadata (flag) | cualquiera de los anteriores con los metadatos y la portada incrustados en la salida (dec-0126, N1) | gratis si ya hay remux |
original con +metadata sobre un fichero sin metadatos fuerza remux: el contenedor de salida
se escribe nuevo de todas formas, así que los tags y la portada entran sin coste extra. Es lo que
hace que "el fichero descargado y reproducido en VLC/AirPlay/lo que sea ya lleva sus
metadatos" (N1) también valga para bibliotecas que no han activado la escritura en origen.
remux, el muxer conoce todas las muestras de antemano (índice del origen, dec-0109):
puede calcular el moov completo y el tamaño final antes de enviar el primer byte.Content-Length exacto y acepta Range sin haber materializado nada;hash(raíz de transferencia ‖ perfil ‖ versión del muxer) y la reanudación con
If-Range funciona aunque el derivado nunca se haya escrito en disco.ExtentMaphash(huella de asset ‖ perfil ‖ versión del motor),
con un ExtentMap (dec-0106): pending mientras se produce, available lo hecho,
known_length + sealed al terminar.pending esperan (cancelable por request, r04), y un corte se reanuda sin re-transcodificar.
Mientras no está sealed la respuesta va sin Content-Length (fMP4 progresivo); una vez
sellado, con tamaño, Range y ETag.r45): un transcode de minutos de CPU no cae por un original que se relee en ms.dec-0110 no lo decide. Este ADR no lo decide tampoco, pero fija una
restricción: sin GPL, así que x264 y x265 quedan fuera. Los candidatos son encoders por
hardware a través de libavcodec (VAAPI, QSV, NVENC, VideoToolbox) y encoders con licencia
permisiva (SVT-AV1, libaom, libvpx, OpenH264), siempre como capability versionada del motor
(r23). La licencia exacta de cada uno la verifica el ticket que lo integre. La política de
códec de salida es CanonicalCodecPolicy (AV1 como production_fallback).GET + Range + If-Range + ETag fuerte + Content-Disposition: attachment; filename*= según RFC 6266). Sin protocolo propio en el cable: el navegador, VLC,
curl, aria2 o el gestor de descargas del sistema reanudan solos.sendfile/splice y kTLS
para el original. La elección por cliente es por medida (T1), no fija. Esto necesita el ADR del
delivery-edge TCP que hoy no existe (ledger V-PLAY-07); este ADR no lo sustituye, lo pide.zkit.blake3_tree, dec-0103), estado de
reanudación persistente ($XDG_STATE_HOME/styx, IndexedDB/OPFS en la web), renovación de la
capability y varias fuentes a la vez (§3.4.5). La usan styx download, la app y la web. No
cambia el wire de conduit: la descarga va por HTTP Range; lo que se añade es el cliente (P2).ByteSource del origen ya es multi-fuente (r26 §2: RAM, NVMe, Storage
Box, peers, HTTP, otro nodo, puntuados y con hedged reads). Una descarga de un asset que vive en
un backend remoto se sirve mientras se lee, con el árbol verificando cada rango.OfflineBundle (r61 D1, dec-0094): un Source adapter (r04), no
un player aparte. Una descarga para offline es un OfflineBundle con un fichero completo
(bundleType: 'mixed' o un tipo nuevo full, P5) más su árbol y los metadatos del cliente.URLSession en segundo plano con el C-ABI de conduit para verificar y reanudar.styx download.dec-0127).OfflineBundle se cifra, la clave se co-diseña con identity (r61 D3). Por
defecto no se cifra lo descargado por su dueño en su dispositivo (P6).track/offline-continuity: las descargas son el primer consumidor real
de OfflineBundle. Eso cambia el caso de dec-0098 (nodo sin consumidor): se propone que el
nodo dependa de la descarga y deje de ser excepción (P1).track/swarm-core): el crate swarm (r26 §4) es, a la vez, un ByteSource
(adquisición, stream mientras descarga) y un egress (sembrar). BitTorrent v1/v2/hybrid; el árbol
de §3.2 es el de v2, así que todo asset es sembrable como v2. seedPolicy sigue r26 §3.dec-0131). No usa DHT ni trackers públicos.dec-0128 (dispositivos).dec-0034 sigue: el Acquisition Runtime sustituye a Sonarr/Radarr/Prowlarr/Bazarr.swarm), HTTP directo, otro Styx (dec-0131), Jellyfin
(outcome/jellyfin-compat), backends remotos (outcome/federated-storage) y la subida del
usuario (dec-0121).cmd.catalog.registerIngested se generaliza con
origin: upload | acquisition | import | federation y la raíz de transferencia. Lo adquirido
entra como AcquisitionArtifact (dec-0019) y se indexa al completar, igual que una subida:
reproducible al momento, canonicalización después y en segundo plano.Enmienda propuesta a la lista de prioridades de r26 §2 (el GlobalByteScheduler no existe en
código; esto fija la política para cuando exista):
interactive-seek > playback-starvation > startup > live-ingest > **download-foreground** > **ingest** > canonicalization-foreground > **transcode-for-download** > torrent-acquisition > **offline-sync** > **peer-upload** > pack-flush > background-scrub > metadata-cleanup
download-foreground: el usuario espera la descarga con la app abierta.ingest: la clase que dec-0121 dejó para un ADR aparte.transcode-for-download: producir un derivado para descarga; cede ante cualquier reproducción.offline-sync: descargas en segundo plano.peer-upload: servir a otros nodos o peers.Ninguna cifra existe hoy (§1). Los umbrales son la propuesta para el gate y los fija waxin (P4).
Todos se miden con un harness nuevo, tools/transfer-bench, en la misma máquina y la misma red
que el referente, con N ≥ 10 ejecuciones y mediana + p95.
| # | Objetivo | Métrica | Referente (misma máquina y red) | Umbral propuesto |
|---|---|---|---|---|
| T1 | Descarga del original a velocidad de línea | goodput y CPU por GiB, 1 y 10 Gbit/s, H3 y TCP | nginx con sendfile + kTLS sirviendo el fichero | ≥ 95 % del goodput de nginx; CPU/GiB ≤ 1,2× nginx |
| T2 | Descarga por WAN con BDP alto | tiempo hasta completar, RTT 50–150 ms con pérdida | aria2 con 8 conexiones | ≤ el de aria2 |
| T3 | Reanudación | bytes reenviados y tiempo hasta el primer byte tras un corte en punto aleatorio | — | bajada: 0 bytes repetidos; subida: ≤ 1 chunk; ≤ 1 RTT + renovar capability |
| T4 | Subida y tiempo hasta reproducible | goodput de subida; tiempo de complete a reproducible y descargable | rsync/scp por SSH + re-scan de Jellyfin | goodput ≥ rsync; reproducible ≤ 2 s tras complete, sin re-scan |
| T5 | Remux al vuelo | tiempo hasta el primer byte y goodput de remux | T1 del original | primer byte ≤ el arranque del stream; goodput ≥ 80 % de T1 |
| T6 | Transcode para descarga | velocidad de producción (× tiempo real) y segunda descarga del mismo perfil | — | segunda descarga a velocidad T1 (sale de caché) |
| T7 | Integridad | bytes corruptos aceptados con inyección de fallos (bit flips, truncado, fuente que miente) | — | 0 |
| T8 | Equidad con la reproducción | arranque y stalls de una reproducción con transferencias concurrentes | la misma reproducción sin transferencias | sin diferencia estadística (p95) |
| T9 | Swarm | tiempo hasta completar el mismo torrent | qBittorrent / libtorrent (r27: varilla SOTA) | ≤ qBittorrent |
Notas:
sendfile, o
sin verificación por bloque en T7) tiene que salir rojo.download (enmienda a dec-0117 I3 y al formato de §4.1): resource =
asset + perfil (o raíz de transferencia + perfil), range = todo el fichero, sid ligado por
IPC como en dec-0121. Vida mayor que la de read porque un gestor de descargas del navegador
reanuda con la misma URL y no puede renovar el token (P7 fija el tope). La revocación sigue
siendo inmediata: el daemon re-autoriza cada petición contra la sesión ligada (I2), y
playback-svc la desliga al revocar.asset:download, distinto de asset:play (dec-0125 §5): el modo público
(dec-0130) y el compartir entre servidores (dec-0131) necesitan "stream sí, descarga no" y
al revés. restricted no lo recibe.dec-0121:
swarm (peer wire, bencode, extensiones) es un parser de entrada
no confiable con su fuzz.Referrer-Policy: no-referrer y Cache-Control: private, no-store sobre la URL firmada.
El riesgo residual (historial del navegador) lo acota el binding al actor y al asset (§7).Content-Disposition con filename* saneado (sin rutas ni controles),
derivado del título del catálogo; el cliente nunca elige una ruta del servidor.evt.security.* (spire), sin el token.dec-0124 §6.1)Ids provisionales; la paridad la comprueba check:headless-parity.
| Operación | Qué | CLI |
|---|---|---|
transfer.upload.open | sesión de ingesta (existe: dec-0121) | styx upload <fichero…|carpeta> |
transfer.download.plan | resolver un DownloadProfile y explicar el plan (r05), sin ejecutarlo | styx download <asset> --profile … --dry-run |
transfer.download.open | firmar la capability y devolver URL(s) y fuentes | styx download <asset> [--profile …] [--out …] |
transfer.list | transferencias del actor (subidas, descargas, offline), con progreso | styx transfers [--json] |
transfer.cancel | cancelar y liberar cuota | styx transfers cancel <id> |
transfer.offline.sync | sincronizar la lista offline de un dispositivo | styx offline sync |
Contratos del socket de control y del bus (nombres provisionales, los fija el ticket con su
consumidor y entran en BUS_ROUTES): cmd.media.openDownload, cmd.media.closeDownload y
qry.media.downloadStatus (playback-svc → daemon); cmd.catalog.registerIngested generalizado
(§3.7).
| # | Riesgo | Mitigación |
|---|---|---|
| R1 | El remux no es determinista (orden de cajas, timestamps de creación, padding) y rompe la reanudación | test de propiedad: dos producciones del mismo perfil dan los mismos bytes; la versión del muxer entra en el ETag; un cambio de muxer invalida |
| R2 | DoS por perfiles caros | presupuesto de transcode por actor, admisión antes de empezar, deduplicación por caché, clase transcode-for-download por debajo de todo playback |
| R3 | La caché L2 se llena de derivados de un solo usuario | tope por actor de derivados no compartidos; desalojo por coste (r45) |
| R4 | Token de vida larga en una URL | binding a actor + asset + perfil, revocación por sesión ligada, sin log, no-referrer; tope de vida (P7) |
| R5 | H3 por debajo de TCP en enlaces rápidos | T1 mide los dos; camino TCP con sendfile/kTLS; requiere el ADR del delivery-edge TCP |
| R6 | Editar metadatos durante una transferencia | lease de transferencia (§3.2); If-Range falla de forma segura |
| R7 | Coste del árbol en bibliotecas grandes | se calcula en la pasada que ya lee el fichero (ingesta, escaneo); outboard regenerable; niveles superiores sólo |
| R8 | Encoders de vídeo con licencia incompatible | sin GPL (x264/x265 fuera); licencia verificada por ticket; capability versionada |
| R9 | Background Fetch sólo en Chromium | descarga en pestaña con reanudación por rangos como fallback; la app y el CLI no dependen del navegador |
| R10 | Derechos y privacidad al sembrar o federar | opt-in por raíz; nada se anuncia por defecto; federación por concesión (dec-0131) |
| R11 | Dos árboles para un asset sembrado (BLAKE3 y SHA-256) | el SHA-256 sólo existe para lo que se siembra; se deriva verificando contra BLAKE3, nunca de bytes sin verificar |
| R12 | have de dedup como oráculo de existencia | dominio de dedup configurable + sin señal entre dominios + cuota por tamaño lógico (conduit v2 §6); server sólo con aviso explícito (lock, 3) |
read de reproducción. Dura 10 minutos y está pensada para un player
que renueva; un gestor de descargas no renueva. Y mezcla "puede ver" con "puede llevárselo".ExtentMap da las dos cosas.V-XFER-01): fuera de banda, sin cuotas por identidad y con re-scan.V-PLAY-07): lo pide, no lo sustituye.dec-0127.dec-0131.dec-0130 y dec-0131.styx.model.yml: P1. track/byte-runtime/egress y
track/media-engine/download-output son alta propuesta queued en el model, pendiente de ratificar en P1; los gate items, no.track/byte-runtime: egress de descarga (original, Range/If-Range, TCP + sendfile/kTLS y
H3), árbol de transferencia, lease, clases del scheduler, scope download en el daemon.track/media-engine: salida progresiva del muxer, remux determinista con tamaño exacto,
transcodificación materializada, +metadata en la salida.track/identity (sec): scope download, permiso asset:download, cuotas de descarga y
transcode.track/offline-continuity: OfflineBundle completo en web, app y CLI.track/swarm-core / track/acquisition-core: el árbol de v2 compartido y la entrada única.dec-0124 §9):
+metadata;download, permiso y cuotas con sus tests de rotura.explicacion/transferencias (estado especificado) se ata a este ADR.r26 §2: clases download-foreground, ingest, transcode-for-download, offline-sync y
peer-upload en la lista de prioridades (§3.8).dec-0117 I3 / §4.1: scope download con su vida y su re-autorización por petición.dec-0121: registerIngested generalizado con origin y raíz de transferencia (§3.7); el
árbol de transferencia en la pasada de ingesta; clientes web y app del mismo wire.Contestadas o diferidas en el lock. Se conservan como registro.
track/byte-runtime/egress (descarga y árbol) y otro en
track/media-engine (salida progresiva y transcode materializado), con TR1–TR8 como gate? ¿Y
track/offline-continuity pasa a depender de la descarga (deja de ser la excepción de
dec-0098)?full en OfflineBundle o mixed?download: ¿cuál es el tope (por ejemplo, horas, ligado a la
duración estimada de la descarga), sabiendo que la revocación es inmediata por la sesión?original, remux y audio transcodificado?draft-ietf-httpbis-resumable-upload-12 (julio 2026):
https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-resumable-upload/Decisión de waxin vía AskUserQuestion (2026-10-02): lockear este ADR con la enmienda de P3 que
propone la lane w13/conduit-v2 (docs/track/byte-runtime/plans/conduit-wire-v2.md §2, sobre la
spec wire v2 de conduit y su ADR 0002-wire-v2-blake3), más un dominio de dedup configurable.
Las demás preguntas de §12 no tuvieron respuesta separada.
BLAKE3(bytes exactos); el outboard pre-order (64 B por 16 KiB) es caché
regenerable en L2, calculada en el único pase de la ingesta o del escaneo;zkit.blake3_tree, que el daemon
consume sin reimplementarla (dec-0103). Mientras zkit no la publique vive en conduit.Authorizer del
IngestSink da el dominio con la SCT ingest, y el dedup (§3.3 punto 6, conduit v2 §5–§6)
sólo responde dentro de él, sin señal entre dominios ni descuento de cuota. Valores:
household (hogar) — por defecto. Los miembros de un hogar comparten dominio. Si el
actor no pertenece a ningún hogar (los hogares son opcionales, dec-0125), su dominio es él
mismo. Consecuencia aceptada: un miembro puede inferir que otro miembro del mismo hogar ya
subió un fichero;user: un dominio por actor. No filtra nada entre usuarios, ni dentro de un hogar. Es
el valor que recomendaba el plan (Q1);server: un dominio para todo el servidor. Ahorra más bytes, pero permite inferir
qué tiene otro usuario (el have del dedup es un oráculo de confirmación de ficheros). La
configuración y el asistente lo muestran con un aviso explícito de ese riesgo, y no se
activa sin confirmarlo.
El dominio es configuración del operador (servidor), no de cada usuario. Cambiarlo no mueve
bytes: sólo cambia a quién responde el have desde ese momento. La cuota se cuenta por tamaño
lógico en cualquier modo (R12).
Invariante que la configuración no relaja (dec-0117, conduit ADR 0002 decisión 5 y su
lock): un hash nunca es una capability de lectura. En un dominio más ancho que lo que el actor
puede leer (household con permisos más finos que el hogar, o server), el riesgo aceptado es
inferir existencia; obtener el objeto sin tener los bytes no se acepta. Para eso hace falta una
prueba de posesión (reto sobre grupos de 16 KiB verificados contra el outboard), que el wire
v2 no tiene. Hasta que conduit la añada, el índice de dedup de styx responde have sólo con
objetos que el actor puede leer dentro del dominio. El dedup ahorra algo menos, pero no hay
fuga ni acceso.r61 D3).original, remux y audio
transcodificado (dec-0110 §1.2). El encode de vídeo sigue sin decidir (§3.4.3, §9) y
entra con su propio ADR, con la restricción sin GPL.tools/transfer-bench cuando mida el primer baseline.download: el ADR no da una cifra, sólo la forma. Se toma esa forma:
tope en horas, ligado a la duración estimada de la descarga, con revocación inmediata por
la sesión ligada (I2). El número lo propone el ticket del scope download (TR8) a waxin
antes de cerrar.styx.model.yml. La forma que
propone el ADR (milestone track/byte-runtime/egress con descarga y árbol, otro en
track/media-engine con salida progresiva y transcode materializado, TR1–TR8 como gate, y
track/offline-continuity dependiente de la descarga) queda como propuesta para la wave que
lo implemente, que la lleva a waxin con check:roadmap.full o mixed en OfflineBundle. Lo decide el primer ticket de
track/offline-continuity que materialice la descarga offline.r26 §2 (clases del scheduler), dec-0117 I3 y
§4.1 (scope download) y dec-0121 (registerIngested generalizado con origin y raíz de
transferencia, árbol en la pasada de ingesta, clientes web y app del mismo wire). Los tres
llevan banner de enmienda desde este lock. Con P3, la raíz de transferencia que lleva
registerIngested es BLAKE3 (plan, §3 y CV2-03).download, permiso asset:download, cuotas) y §6 (superficie headless).El lock desbloquea el código de producción que depende de este ADR. La ingesta v2 de conduit y
los tickets CV2-01..CV2-05 del plan cuelgan de track/byte-runtime sin nodo nuevo.
r26, dec-0117 y dec-0121 llevan banner de enmienda desde el lock (2026-10-02).docs/track/byte-runtime/plans/conduit-wire-v2.md (rama w13/conduit-v2) y
conduit docs/decisions/0002-wire-v2-blake3.md.dec-0126 §4.3: el lease de transferencia (§3.2) amplía sus exclusiones.V-N07, V-N10, V-XFER-01, V-XFER-02, V-XFER-03, V-XFER-04, V-XFER-05
(docs/overview/vision-ledger.yml).dec-0019, dec-0034, dec-0094, dec-0098, dec-0106, dec-0110,
dec-0124, dec-0125; reservados dec-0127, dec-0128, dec-0130, dec-0131.dec-0128
Vista generada de dec-0128: Dispositivos, presencia, mando remoto, handoff y capa de cast
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