PRIVATE ALPHA

Managed SRT pull

Start and supervise an SRT source connection from this node.

input.srt.pull

input.srt.pull dials an SRT source as a caller. The API and startup list are independent: api.enabled exposes /api/v1/pulls, while static applies entries once during process startup.

A transport disconnect keeps the logical pull alive during bounded retry. Exhausting the retry deadline is terminal until an API request or process restart creates the pull again.

When traditional HLS output is enabled, reconnecting transport sessions share one process-local sequence and discontinuity lineage for the logical pull. The public playlist path stays unchanged, while each successor session publishes segments from a new incarnation namespace. An explicit DELETE followed by a new PUT creates a new lineage.

"pull": {
  "api": {"enabled": true},
  "maxActivePulls": 8,
  "static": [{
    "streamId": "${STREAM_ID}",
    "source": "${SOURCE_SRT_URL}",
    "srtStreamId": "${SOURCE_STREAM_ID}",
    "passphrase": "${SOURCE_PASSPHRASE}"
  }]
}

Static entries are startup input rather than continuous reconciliation. Deleting a static pull through the API does not recreate it until the process starts again.

Fenced operation API

The operation API is the control-plane contract for a scheduler that must recover after losing an HTTP response. It is process-local. A different originIncarnation never proves that an operation against the previous process stopped; the caller must keep that outcome unresolved until the old process is proven dead.

GET /api/v1/pull-control/capabilities returns:

{
  "schemaVersion": 1,
  "operationContract": "v1",
  "originIncarnation": "00000000-0000-4000-8000-000000000001",
  "writerEpoch": "",
  "lastSequence": "0",
  "maxRetainedOperations": 128
}

The UUID values in this page are shape examples. Each process and writer must use newly generated random UUIDs. PUT /api/v1/pull-control/writer performs a global compare-and-swap with originIncarnation, expectedWriterEpoch, and newWriterEpoch. The process-wide decimal lastSequence never resets when the writer changes. A writer fence and every operation commit use the same controller ownership lock.

PUT /api/v1/pull-operations/{operationId} accepts:

{
  "originIncarnation": "00000000-0000-4000-8000-000000000001",
  "writerEpoch": "00000000-0000-4000-8000-000000000002",
  "sequence": "1",
  "kind": "start",
  "streamId": "tenant:stream",
  "expectedInstanceId": null,
  "spec": {"source": "srt://source.example:9000"}
}

A start requires explicit expectedInstanceId: null and a pull spec. A stop requires the exact instanceId reported by the pull inventory and forbids a spec. The instance ID is stable for the logical pull while transport generations change on reconnect. All decimal sequence strings are canonical: 1 is valid, while +1, 01, and 0 are rejected.

The result contains operationId, originIncarnation, writerEpoch, sequence, kind, streamId, state, and optional instanceId and errorCode. A start returns committed. A stop first returns draining, then becomes fully_drained only after dial, media worker, sink, output scope, and registry ownership have all been released. A registry release error produces failed with drain_failed and keeps its admission budget quarantined. An exact stop can also clear a retained terminal-failure tombstone as fully_drained when its registry ownership was released. A mismatched instance ID is rejected, and a tombstone whose registry ownership remains reserved returns failed with drain_failed.

Process shutdown seals new legacy and operation admission, stops active pulls, and waits within the shutdown context for pulls that an earlier stop already removed from inventory. A context timeout remains a reported shutdown error; it never turns an outstanding drain into fully_drained.

An expected-instance mismatch and a pull-limit rejection happen before any pull side effect. Their retained result is not_committed with the exact operation identity and stable error code. A recovering writer may use that proof only after fencing the writer epoch that submitted it; a current writer must still allow for a delayed request.

GET /api/v1/pull-operations/{operationId} requires the exact originIncarnation, writerEpoch, and sequence query tuple. A retained exact record remains queryable after a later writer fence. A missing operation from an older fenced writer with a sequence above the process watermark returns fenced. A missing sequence at or below the watermark returns failed with unknown_retention. A missing operation for the current writer returns failed with operation_unknown, because a delayed request may still arrive. The synthetic results omit kind and streamId. Retained committed, fully_drained, and not_committed results carry the complete operation identity. Unknown, retention, and incarnation-mismatch outcomes are fail-closed.

The ledger retains at most 128 records. Terminal records are evicted in sequence order. A draining record cannot be evicted; if every slot is non-terminal, a new operation is rejected before its sequence or side effect commits. An identical operation ID and canonical request replays the retained result. Reusing an ID with different input, or reusing a consumed sequence under another ID, is rejected.

New committed starts return HTTP 201, newly accepted stops return HTTP 202, and retained or queried results return HTTP 200. Errors use the normal JSON error envelope with these stable codes:

HTTP Code Meaning
400 invalid_request The operation envelope, UUID, sequence, or shape is invalid.
400 invalid_pull_spec The nested start spec is invalid.
409 stale_origin The requested origin incarnation is not this process.
409 fenced The writer epoch is no longer current.
409 operation_conflict A retained operation ID has different input.
409 sequence_reused The process watermark already consumed the sequence.
409 instance_precondition_failed Expected absence or exact instance ownership did not match.
429 operation_capacity Every retained ledger slot is non-terminal.
429 pull_limit Active and draining pull ownership reached its configured limit.
500 operation_failed An internal operation step failed before a safe result was available.
503 control_closed Process shutdown permanently closed mutation admission.

See the shared input contract and complete configuration.

Configuration reference

The complete field reference covers every accepted option; the maximal composition shows how modules fit together.