Protocolossession-ipc (control Bun ↔ daemon)messages

Ingesta — cmd.media.openIngest, cmd.media.pollIngestEvents, cmd.media.closeIngest

Spec canónica de protocols/session-ipc/messages/ingest.md, copiada sin reescribir.

ImplementadoSin versión del tren todavía· generada desde protocols/session-ipc/messages/ingest.md

Página generada desde protocols/session-ipc/messages/ingest.md. No se edita a mano: bun run docs:gen la regenera y bun run docs:check falla si difiere.

Ingesta — cmd.media.openIngest, cmd.media.pollIngestEvents, cmd.media.closeIngest

Subida de ficheros a un nodo con el wire de conduit (dec-0121). Bun decide quién sube (playback-svc, tras resolver el access token del usuario), el daemon ejecuta: liga una sesión de ingesta en la autoridad de capabilities (spire.cap) y reporta las subidas terminadas. Los bytes nunca pasan por este socket: el cliente sube al IngestSink del daemon con la SCT ingest de la sesión.

Contratos: packages/api-contracts/src/bus/media-daemon.ts (MediaOpenIngest, MediaPollIngestEvents, MediaCloseIngest), validados por spire a los dos lados. Emisores admitidos (BUS_ROUTES): playback-svc y styx-ops; cualquier otro, status = denied.

cmd.media.openIngest

{ "capActor": "<32 hex: actorOf(actorId)>", "capResource": "<64 hex: Resource.ingestRoot(rootId)>" }

Liga ing_<32 hex> (16 bytes aleatorios) al actor y a la raíz de ingesta del daemon. La SCT de scope ingest de esa sesión (sid = esos 16 bytes, resource = la raíz, rango bytes [0, max) = tope del fichero) es lo único que el sink acepta.

{ "result": { "sessionId": "ing_…", "endpoint": "https://node/api/upload", "rootId": "ingest" } }
error.codeCuándo
INGEST_UNAVAILABLEel daemon no tiene ingesta (STYX_INGEST_ROOT sin definir) o autoridad
INGEST_RESOURCE_MISMATCHcapResource no es la raíz de este daemon (no se liga nada)
CAPABILITY_BINDING_INVALIDdigests mal formados
TOO_MANY_SESSIONSla tabla de sesiones de la autoridad está llena (recoverable: true)
CAPABILITY_BIND_FAILEDel id ya estaba ligado

Si la respuesta no se puede sellar, spire deshace el binding (onReplyFailure): nadie tendría el id para cerrarla.

cmd.media.pollIngestEvents

{ "ackSeq": 0 }

Confirma (y olvida) todo evento con seq <= ackSeq y devuelve los más antiguos sin confirmar, como mucho 32:

{
  "events": [
    {
      "seq": 1,
      "sessionId": "ing_…",
      "actorDigest": "<32 hex>",
      "uploadId": "<32 hex>",
      "sizeBytes": 1234,
      "blake3": "<64 hex: raíz BLAKE3 del fichero, lo que imprime b3sum>",
      "rootId": "ingest",
      "key": "<64 hex: nombre bajo la raíz = blake3>",
      "filename": "nombre del cliente (metadata)"
    }
  ]
}

Versión 2 del contrato (la 1 llevaba sha256 y se rechaza): la ingesta habla conduit wire v2 (sólo v2, dec-0121), así que la raíz que el daemon verificó al recibir es la BLAKE3 en árbol de conduit (dec-0129), la misma que imprime b3sum. Es la identidad de los bytes transferidos, no la del asset (dec-0127 deriva AssetId de la huella del payload).

Un evento sólo sale de la cola cuando se confirma: entrega al menos una vez. Con la cola llena, el complete del cliente responde 500 y se reintenta. playback-svc confirma un evento cuando catalog lo registró (cmd.catalog.registerIngested) o lo rechazó para siempre. Sin ingesta: { "error": { "code": "INGEST_UNAVAILABLE", … } }.

cmd.media.closeIngest

{ "sessionId": "ing_…" }

Desliga la sesión en la autoridad: todo token suyo deja de pasar en la siguiente petición al sink (revocación). Después el sink olvida el token de la sesión y aborta sus subidas sin terminar: el hueco del actor, la reserva de staging y el .part vuelven en el acto (una que ya renombra su fichero termina y se retira después). Idempotente: { "closed": false } si ya no estaba ligada; lo que quedara de la sesión en el sink se aborta igual.