EnfinitOSEnfinitOS
DevelopersVisual render
Production-ready scaffold

CTV App SDK

Reference SDK across Roku, Samsung Tizen, LG webOS, Fire TV / Android TV. Same core, four thin wrappers.

@enfinitos/sdk-ctv-appSubstrate CTVTypeScript, BrightScript, JavaScript
Install

Get the SDK

npm install @enfinitos/sdk-ctv-app

About this status badge

Typed, tested, documented, and grounded in the 2026 platform reality. Awaiting first customer-integration validation.

README

The developer-facing documentation in full

Rendered from packages/sdks/ctv-app/README.md at build time — the same source the package ships with.

@enfinitos/sdk-ctv-app

EnfinitOS reference SDK for the CTV substrate — TypeScript core plus four per-platform thin wrappers covering the four CTV hardware platforms that dominate the market:

  • Roku (BrightScript)
  • Samsung Tizen (JavaScript on WebKit)
  • LG WebOS (JavaScript on WebKit)
  • Apple TV / tvOS (Swift on UIKit/SwiftUI)

The SDK is what a CTV app embeds to plug its inventory into the EnfinitOS rights / consent / proof-of-play / audit plane.

Architecture

                       ┌──────────────────────────┐
                       │   EnfinitOS Platform     │
                       │  (runtime, rights,       │
                       │   audit, fleet health)   │
                       └────────────┬─────────────┘
                                    │  HTTPS REST
                                    │  (device JWT)
                                    │
   ┌────────────────────────────────▼─────────────────────────┐
   │                                                          │
   │   ┌──────────────────────────────────────────────────┐   │
   │   │       core/  (TypeScript — ~2000 lines)          │   │
   │   │                                                  │   │
   │   │   ┌──────────────────────┐  ┌─────────────────┐  │   │
   │   │   │ EnfinitOSCtvClient   │  │ ProofReporter   │  │   │
   │   │   │  (renderer-core +    │  │ (proof-of-play  │  │   │
   │   │   │   fetchAdSlot /      │  │  envelope ride  │  │   │
   │   │   │   reportPlay* /      │  │  on existing    │  │   │
   │   │   │   reportInteraction) │  │  event-ingest)  │  │   │
   │   │   └──────────────────────┘  └─────────────────┘  │   │
   │   │                                                  │   │
   │   │   ┌─────────────────────────────────────────┐    │   │
   │   │   │ ViewabilityScorer                       │    │   │
   │   │   │  (MRC-style score from 6 signals)        │    │   │
   │   │   └─────────────────────────────────────────┘    │   │
   │   └──────────────────────────────────────────────────┘   │
   │                          ▲                               │
   │                          │                               │
   │   ┌──────────────────────┴──────────────────────────┐    │
   │   │       platforms/  (~250 lines each)             │    │
   │   │                                                 │    │
   │   │     Roku        Tizen     WebOS    Apple TV     │    │
   │   │   (BrightScript) (JS)     (JS)     (Swift)      │    │
   │   │                                                 │    │
   │   │   Each: lifecycle wiring, remote-key mapping,   │    │
   │   │   native-API signals, thin facade over core.    │    │
   │   └─────────────────────────────────────────────────┘    │
   └─────────────────────┬────────────────────────────────────┘
                         │
                         ▼
              CTV hardware
              (smart TV / set-top box / streaming stick)

Why a TS core + four thin wrappers

Three of the four CTV platforms (Tizen / WebOS / Apple TV) can host the TypeScript core through their JS runtimes or through native-side plumbing. The fourth (Roku) cannot — BrightScript is the only language Roku channel apps speak.

Rather than build four parallel implementations, we ship:

  1. The TS core with all the substantive logic — fetch / resolve / grant / event reporting / viewability scoring / proof-of-play assembly / DRM probe scaffolding. Tested with full vitest coverage. ~2000 lines.
  2. Per-platform wrappers that handle each platform's lifecycle and remote-control surface. Each ~150-300 lines, thin enough that their semantics are 1:1 with the TS core.

The Roku BrightScript wrapper is the only platform where the substrate-side code re-implements logic from the TS core (because BrightScript can't import the JS module); it mirrors the TS core's behaviour function-for-function and ships a roca-test target.

SDKs

SDKLocationLanguageTests
TS corecore/TypeScriptvitest (4 test files, ~80 cases)
Roku wrapperplatforms/roku/BrightScript(substantive coverage on TS core)
Tizen wrapperplatforms/tizen/JavaScript(substantive coverage on TS core)
WebOS wrapperplatforms/webos/JavaScript(substantive coverage on TS core)
Apple TV wrapperplatforms/appletv/SwiftXCTest skeleton (Tests/EnfinitOSCtvTests.swift)

Getting started — TS core

pnpm add @enfinitos/sdk-ctv-app-core
import { EnfinitOSCtvClient } from "@enfinitos/sdk-ctv-app-core";

const client = new EnfinitOSCtvClient({
  apiBaseUrl: "https://api.enfinitos.com",
  appId: "app_streamcorp_v3",
  deviceId: getDeviceId(),
  deviceToken: getDeviceToken(),
  deviceClass: "tizen",                    // one of "roku" | "tizen" | "webos" | "appletv"
  viewerId: getPseudonymousSessionId(),    // optional
});

await client.start();

const asset = await client.fetchAdSlot("pre-roll", {
  contentCategory: "sports",
});
if (asset) {
  await client.reportPlayStart(asset);
  client.setDisplayOn(true);
  client.setAppForeground(true);
  client.setAudioOn(true);
  // ... feed asset.assetUrl into your player ...
  // On each positionUpdate from the player:
  client.recordPosition(currentS, expectedS);
  // ... slot ends ...
  const viewability = client.buildViewability();
  await client.reportPlayComplete(asset, viewability);
}

API surface

Constructor

new EnfinitOSCtvClient({
  apiBaseUrl: string;
  appId: string;
  deviceId: string;
  deviceToken: string;
  deviceClass: "roku" | "tizen" | "webos" | "appletv";
  viewerId?: string;
  onAssetReady?, onError?: callbacks
})

Lifecycle

MethodDescription
start() / stop()Open / close the platform session.

Resolve

MethodDescription
fetchAdSlot(position, extra?)Resolve+grant an ad slot.

Reporting

MethodDescription
reportPlayStart(asset)Start of impression.
reportPlayComplete(asset, viewability)End + proof-of-play envelope.
reportInteraction(asset, interaction)Viewer-driven interaction.
reportPlayError(asset, error)Non-billable error.

Signals (forwarded into the scorer)

MethodDescription
setDisplayOn(on)Hardware power signal.
setAppForeground(fg)App lifecycle signal.
setAudioOn(on)Audio path on/off.
recordPosition(positionS, durationS)Quartile + dwell tick.
buildViewability()Snapshot current viewability score.

Health + introspection

MethodDescription
reportHealth(state, subsystems?)Substrate-agnostic heartbeat.
startHealthHeartbeat(ms)Periodic heartbeat.
queueDepth() / droppedEventCount()Renderer-core introspection.
rendererCore / viewability / proofReporterDirect access to composed instances.

Viewability scoring

The CTV substrate can't measure pixel-level visibility (no Intersection Observer; no in-tab visibility API). The SDK substitutes six platform-observable signals fused into a 0-1 score:

SignalSourceDefault weight
displayOnplatform power query0.30
appForegroundplatform lifecycle hook0.30
audioOnplayer muted state0.20
q2Reachedscorer.recordPosition0.10
q4Reachedscorer.recordPosition0.10

The MRC-flavoured "isMrcViewable" predicate requires displayOn, appForeground, q2Reached, and dwellMs >= 2000.

Proof-of-play

Every reportPlayComplete call submits a ProofOfPlay envelope:

{
  asset: ResolvedAsset;
  startedAt: string;
  endedAt: string;
  viewability: ViewabilityScore;
  completed: boolean;        // === viewability.q4Reached
  correlationId?: string;    // optional pod/cue id
}

The envelope rides the renderer-core's interaction-event path under kind: "proof_of_play" so the platform's existing audit-ledger absorbs it without a new endpoint.

Platform wrapper integration

PlatformWrapperHow it's loaded
RokuMain.brsCopied into channel's source/ folder
Tizenindex.jsImported via npm into the Tizen .wgt
WebOSindex.jsImported via npm into the WebOS app
Apple TVEnfinitOSCtv.swiftSwift Package or copied source

Each wrapper README documents the platform-specific configuration, remote-key mapping, and Tested-versions matrix.

Tests

cd core && pnpm test

The vitest suite covers four areas (~80 test cases):

AreaTests
viewabilityScorer.tsScore formula, weight overrides, clamping, quartile tracking, dwell, MRC predicate, reset.
proofReporter.tsEnvelope submission, validation, timestamp inversion, score out-of-range.
ctvCore.tsArgument validation, lifecycle, fetchAdSlot context shape, play lifecycle, signal forwarding, health, introspection.

The XCTest skeleton at platforms/appletv/swift/Tests/EnfinitOSCtvTests.swift covers the Swift wrapper's construction validation and viewability scoring. The BrightScript / Tizen / WebOS wrappers are thin enough that the TS core's coverage applies semantically; smoke-tested against the native runtimes during integration.

What's SDK-side vs platform-side

ConcernThis SDKPlatform
CTV slot vocabulary(ride existing resolve context)
Viewability scorer(audit ledger records the score)
Proof-of-play envelope✅ rides interaction events✅ existing event-ingest
Per-platform lifecycle wiring✅ in wrappers
Remote-control key mapping✅ in wrappers
DRM key fetching⏳ future tranche⏳ future tranche
Substrate-agnostic primitives(inherits from renderer-core)✅ existing runtime plane

See also

  • packages/sdks/renderer-core/README.md — substrate-agnostic foundation.
  • packages/sdks/dooh-renderer/README.md — sibling SDK for DOOH panels.
  • packages/sdks/streaming-player/README.md — sibling SDK for HLS / DASH / WebRTC streaming.
  • docs/launch/substrate-readiness-matrix.md — CTV substrate readiness.
API reference

Hit the HTTP surface directly

The CTV App SDK is a thin client over the same governed HTTP API every other SDK calls. The full OpenAPI 3.1 reference lives on the docs site.

Sandbox

Run this SDK against a real tenant

The hosted sandbox is the fastest way to verify CTV App SDK against a real EnfinitOS tenant before committing to a pilot. Launching Q4 2026.