Sluby
REST APIDelivery

Master HLS manifest for an asset or playback ID.

A private asset or a signed-policy playback ID needs the `expires` and `sig` of the asset’s signed master url; the child urls in the answer are then signed too. A signed playback ID gates only this entry: on a public asset its objects stay public, so the bytes are not private (make the asset private for that), and other embeds of the asset keep playing. A public answer carries an ETag and `public, no-cache`, but no shared cache keeps it: each 200 GET counts as a play (see `play_count` on an asset); HEAD and 304 do not.

GET
/v1/stream/{videoAssetId}/master.m3u8

A private asset or a signed-policy playback ID needs the expires and sig of the asset’s signed master url; the child urls in the answer are then signed too. A signed playback ID gates only this entry: on a public asset its objects stay public, so the bytes are not private (make the asset private for that), and other embeds of the asset keep playing. A public answer carries an ETag and public, no-cache, but no shared cache keeps it: each 200 GET counts as a play (see play_count on an asset); HEAD and 304 do not.

Path Parameters

videoAssetId*string

Asset UUID or pb_ playback ID.

Query Parameters

expires?integer

Expiry (unix seconds) from the signed master URL. Required when gated.

sig?string

Signature from the signed master URL. Required when gated.

Response Body

HLS manifest.

application/vnd.apple.mpegurl
Example Request
curl -X GET 'https://sluby.example.com/v1/stream/string/master.m3u8'
Example Response
Empty

Stream a Sia object, with byte-range support. GET

An object of a public asset needs nothing. An object of a private asset needs the `expires` and `sig` of a signed url (GET /api/v1/playback/{id}/signed); a manifest served that way has the urls of its children signed with the same expiry. Access follows only the asset tier: a signed playback ID gates its own /v1/stream entry, not these objects. HEAD answers the same headers without a body. A public answer carries `ETag` (the object id) and `Cache-Control: public, no-cache`, and a conditional request (`If-None-Match`) is answered 304 only after access is checked again, so a cache never keeps serving an object after its asset turns private or is deleted; the shipped nginx keeps small public objects for at most 60 seconds between checks. Objects over 8 MB are streamed and never cached.

Per-object sia:// share URLs for direct-from-Sia playback, without credentials. GET

Gated like /v1/stream: a public asset by its id or a public playback ID needs nothing. A private asset or a signed-policy playback ID needs the `expires` and `sig` of its signed master URL, and the shares then never outlive `expires`. Expiries are capped like the authenticated route (six hours, one hour when gated) and rounded to five-minute slots that the server caches, so repeated calls do not mint again. Every map is sent `no-store` (`private, no-store` when gated): minting one is how a direct-from-Sia play reaches the server, and each counts as a play (see `play_count` on an asset), unless `renew` is set. Share URLs already handed out keep working until they expire, whatever happens to the asset meanwhile.