> ## Documentation Index
> Fetch the complete documentation index at: https://docs.livepeer.org/llms.txt
> Use this file to discover all available pages before exploring further.

# BYOC architecture

> BYOC container architecture: FrameProcessor interface, StreamServer, Docker packaging, aiModels.json registration, and the orchestrator-to-container flow.

export const CenteredContainer = ({children, maxWidth = "800px", padding = "0", preset = "default", width = "", minWidth = "", marginRight = "", marginBottom = "", textAlign = "", style = {}, className = "", ...rest}) => {
  const presets = {
    default: {},
    fitContent: {
      width: "fit-content",
      minWidth: "fit-content"
    },
    readable70: {
      width: "70%",
      minWidth: "fit-content"
    },
    readable80: {
      width: "80%",
      minWidth: "fit-content"
    },
    readable90: {
      width: "90%"
    },
    wide900: {
      maxWidth: "900px"
    }
  };
  const presetStyle = presets[preset] || presets.default;
  return <div className={className} style={{
    maxWidth: presetStyle.maxWidth || maxWidth,
    margin: "0 auto",
    padding: padding,
    ...presetStyle.width ? {
      width: presetStyle.width
    } : {},
    ...presetStyle.minWidth ? {
      minWidth: presetStyle.minWidth
    } : {},
    ...width ? {
      width
    } : {},
    ...minWidth ? {
      minWidth
    } : {},
    ...marginRight ? {
      marginRight
    } : {},
    ...marginBottom ? {
      marginBottom
    } : {},
    ...textAlign ? {
      textAlign
    } : {},
    ...style
  }} {...rest}>
      {children}
    </div>;
};

export const CustomDivider = ({color = "var(--lp-color-border-default)", middleText = "", spacing = "default", style = {}, className = "", ...rest}) => {
  const spacingPresets = {
    default: {
      margin: "24px 0"
    },
    overlap: {
      margin: "-1rem 0 -1rem 0"
    },
    tight: {
      margin: "0 0 -1rem 0"
    },
    section: {
      margin: "0 0 -2rem 0"
    },
    sectionOverlap: {
      margin: "-1rem 0 -2rem 0"
    },
    deepOverlap: {
      margin: "-1rem 0 -1.5rem 0"
    }
  };
  const spacingStyle = spacingPresets[spacing] || spacingPresets.default;
  return <div role="separator" aria-orientation="horizontal" className={className} style={{
    display: "flex",
    alignItems: "center",
    ...spacingStyle,
    fontSize: style?.fontSize || "16px",
    height: "fit-content",
    ...style
  }} {...rest}>
      <span style={{
    marginRight: "var(--lp-spacing-px-8)",
    opacity: 0.2
  }}>
        <Icon icon="/snippets/assets/logos/Livepeer-Logo-Symbol-Theme.svg" />
      </span>
      <div style={{
    flex: 1,
    height: "1px",
    background: "var(--lp-color-border-default)",
    opacity: 0.4
  }}></div>
      {middleText && <>
          <Icon icon="circle" size={2} />
          <span style={{
    margin: "0 8px",
    fontWeight: "bold",
    color: color,
    opacity: 0.7
  }}>
            {middleText}
          </span>
          <Icon icon="circle" size={2} />
        </>}
      <div style={{
    flex: 1,
    height: "1px",
    background: "var(--lp-color-border-default)",
    opacity: 0.4
  }}></div>
      <span style={{
    marginLeft: "var(--lp-spacing-px-8)",
    opacity: 0.2
  }}>
        <span style={{
    display: "inline-block",
    transform: "scaleX(-1)"
  }}>
          <Icon icon="/snippets/assets/logos/Livepeer-Logo-Symbol-Theme.svg" />
        </span>
      </span>
    </div>;
};

<CenteredContainer preset="readable90">
  <Tip>A BYOC container is a Docker image that implements FrameProcessor. The Orchestrator loads it, routes trickle frames to it, and advertises its capabilities to Gateways via the AI Service Registry.</Tip>
</CenteredContainer>

<CustomDivider />

BYOC (Bring Your Own Container) lets you deploy custom AI models on the Livepeer Network. The architecture has three layers: your model code wrapped in a `FrameProcessor`, a `StreamServer` that handles trickle transport, and a Docker container that the Orchestrator manages.

<CustomDivider />

## Three-layer architecture

```
Gateway
  │ live-video-to-video request
  ▼
Orchestrator (go-livepeer)
  │ routes to container via Docker socket
  ▼
BYOC Container (Docker)
  ├── StreamServer (PyTrickle)
  │     ├── trickle subscribe (input frames)
  │     └── trickle publish (output frames)
  └── FrameProcessor (your model code)
        └── process_frame(VideoFrame) -> VideoFrame
```

**FrameProcessor** is the interface you implement. It receives `VideoFrame` objects (raw pixel data + PTS timestamp) and returns processed `VideoFrame` objects. Your model logic goes in `process_frame()`.

**StreamServer** wraps the FrameProcessor and handles trickle transport: subscribing to input frames, calling your processor, and publishing output frames. PyTrickle provides this out of the box.

**Docker container** packages the StreamServer, FrameProcessor, model weights, and dependencies. The Orchestrator manages the container lifecycle via the Docker socket.

<CustomDivider />

## Capability registration

The Orchestrator advertises your container's capabilities to the network through `aiModels.json`:

```json theme={"theme":{"light":"github-light","dark":"dark-plus"}}
{
  "pipeline": "live-video-to-video",
  "model_id": "my-custom-model",
  "price_per_unit": 3000,
  "warm": true,
  "container": "my-registry/my-model:latest",
  "url": "http://localhost:8100"
}
```

| Field            | Description                                                       |
| ---------------- | ----------------------------------------------------------------- |
| `pipeline`       | Always `live-video-to-video` for BYOC containers                  |
| `model_id`       | Your chosen model identifier; Gateways use this to route requests |
| `price_per_unit` | Wei per compute unit (Orchestrator-set)                           |
| `warm`           | Whether the model is pre-loaded in GPU memory                     |
| `container`      | Docker image reference                                            |
| `url`            | Local HTTP endpoint where the container listens                   |

Gateways discover your capability through the AI Service Registry on-chain and the Orchestrator's `GetOrchestratorInfo` response.

<CustomDivider />

## Health check contract

Every BYOC container must expose a `/health` endpoint:

```python theme={"theme":{"light":"github-light","dark":"dark-plus"}}
@app.get("/health")
async def health():
    return {"status": "ok"}
```

The Orchestrator polls `/health` to determine container readiness. A container that does not return `{"status": "ok"}` is not advertised to Gateways.

<CustomDivider />

The [BYOC quickstart](/v2/developers/build/compute/byoc/byoc-quickstart) walks through building and registering a container. The [FrameProcessor reference](/v2/developers/build/ai-and-agents/realtime-ai/pytrickle/frame-processor) covers the processing API.
