Solution: TAMS storage - EyevinnOSC/community GitHub Wiki

Getting Started

TAMS Gateway implements the Time-addressable Media Store (TAMS) specification: it stores segmented media flows by combining an S3-compatible object store for the media segments with a database index (CouchDB) for those segments. This guide uses the TAMS Gateway Solution, a one-click deployment that provisions the gateway together with its CouchDB and MinIO backing services as a single stack, then walks through using the API and playing back media.

Prefer to run the gateway against your own (or an existing) CouchDB and S3 storage? Use the TAMS Gateway service guide instead, which sets up the single gateway service by hand.

What the Solution deploys

The Solution composes three OSC services into one stack:

Service Role
MinIO S3-compatible object store for the media segments
CouchDB segment and flow metadata index
TAMS Gateway the TAMS REST API

It also creates the S3 bucket in MinIO before the gateway starts (the gateway never creates buckets itself) and wires the three services together, so you do not configure any connection strings by hand.

Prerequisites

  • If you have not already done so, sign up for an Eyevinn OSC account
  • Enough room in your plan for the three services (or purchase them individually)

Step 1: Deploy the Solution

Option A: One-click in the web console

In the OSC web console, open Browse and select Solutions, then choose TAMS Gateway. Provide the deployment inputs and deploy:

Input Required Description
tams_name optional (default tams) Instance name shared by the stack. Lower-case letters and numbers only.
tams_bucket optional (default tams) S3 bucket for the media objects. Created in MinIO by the Solution. Use a dot-free name.
minio_username required MinIO root user. Supplied to the gateway as the S3 access key id.
minio_password required MinIO root password (min 10 chars, with lower + upper + digit). Supplied as the S3 secret key.
couchdb_password required CouchDB admin password (the gateway's database password).

When the stack is up, the deployment outputs the gateway, MinIO, and CouchDB instance URLs. The TAMS API is served at the gateway instance URL.

Option B: Terraform / OpenTofu

The Solution is an open Terraform composition using the EyevinnOSC/osc provider. The source lives in EyevinnOSC/terraform-examples at examples/tams-pipeline. You need Terraform (or OpenTofu) >= 1.6.0 and aws-cli + bash on the machine running apply (the bucket is created via a small provisioner script), plus an OSC Personal Access Token.

terraform init
terraform apply \
  -var "osc_pat=<YOUR_OSC_PAT>" \
  -var "tams_name=mytams" \
  -var "tams_bucket=mytams" \
  -var "minio_username=<min 3 chars>" \
  -var "minio_password=<min 10 chars, upper+lower+digit>" \
  -var "couchdb_password=<strong password>"

Step 2: Create a Service Access Token (SAT)

On OSC the gateway runs behind the OSC ingress access gate, which authenticates every request by validating a Service Access Token (SAT) before it reaches the gateway. The gateway does not use its own API token in this deployment.

Open Settings (bottom left of the OSC console), go to the API tab, and copy your Personal Access Token (PAT). Then, with npm installed, mint a SAT:

export OSC_ACCESS_TOKEN=<YOUR_PAT>
npx -y @osaas/cli service-access-token eyevinn-tams-gateway

Send the SAT on every API request as Authorization: Bearer <SAT> (or on the x-jwt header, or as a {serviceId}.sat cookie). A request without a valid SAT returns 401 from the gate. The SAT is short-lived (about 1 hour); automated clients should re-mint it (or refresh lazily on a 401).

Interactive API documentation (Swagger UI) is served at <gateway-url>/docs. Because the gate protects every path, open it with a client that sends the SAT header.

Step 3: Create a flow

The goal is to register a flow (stored in CouchDB), upload its media segments (to the MinIO store), and then read them back for playback. A flow is created or updated with a PUT to /flows/{id}; the gateway creates the matching source automatically.

GW=https://<gateway-url>
AUTH="Authorization: Bearer <SAT>"

curl -X PUT "$GW/flows/<flow-id>" -H "$AUTH" -H "Content-Type: application/json" -d '{
  "id": "<flow-id>",
  "source_id": "<source-id>",
  "format": "urn:x-nmos:format:video",
  "codec": "video/mp2t"
}'

format and codec are required. Use video/mp2t (MPEG-TS) if you want the built-in HLS playback in Step 5.

Step 4: Store segments

First allocate storage with a POST (empty body) to /flows/{id}/storage. The response contains a media_objects array, each entry holding an object_id and a presigned put_url:

curl -X POST "$GW/flows/<flow-id>/storage" -H "$AUTH"
{
  "media_objects": [
    {
      "object_id": "<bucket>/<uuid>",
      "put_url": { "url": "https://.../<bucket>/<uuid>?X-Amz-..." }
    }
  ]
}

Upload each media segment with an HTTP PUT directly to its put_url.url. Then register each stored segment with a POST to /flows/{id}/segments, recording its object_id and timerange:

curl -X POST "$GW/flows/<flow-id>/segments" -H "$AUTH" -H "Content-Type: application/json" -d '{
  "object_id": "<object_id from the storage response>",
  "timerange": "[0:0_2:0)"
}'

The timerange uses the TAMS notation [<seconds>:<nanoseconds>_<seconds>:<nanoseconds>) (TAI). For example [0:0_2:0) is the first two seconds of media.

Step 5: Fetch segments and play back

Read a flow's segments for a timerange. The response returns presigned get_urls you can use to download each segment:

curl "$GW/flows/<flow-id>/segments?timerange=[0:0_2:0)" -H "$AUTH"

Segment listings are paged: pass limit to cap the page size and follow the Link: <...>; rel="next" response header for the next page.

For MPEG-TS flows you do not have to assemble a player input yourself. The gateway synthesises a ready-to-play HLS playlist on the fly:

GET /flows/{id}/output.m3u8?type=vod

Use ?type=vod for a complete playlist (ends with #EXT-X-ENDLIST), ?type=live for the live edge, and ?timerange=[start_end) to restrict the window. The playlist is playable in any standard HLS client (hls.js, Safari, Omakase Player). Only MPEG-TS (H.264/AAC) flows are playable; other containers return 415. For browser playback, set CorsOrigin on the gateway and allow GET + Range on the object store from the player origin.

The gateway also serves a built-in, read-only inspector UI at /ui that browses sources, flows, and segments and plays a flow with an embedded HLS player. It inherits the deployment's auth, so it is most directly reachable when the gateway is run standalone or behind a browser-friendly front; behind the OSC gate a plain browser is challenged for the SAT on every path.

Additional information

⚠️ **GitHub.com Fallback** ⚠️