> ## 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.

# The Livepeer Open-AI Infrastructure Stack

> The architecture of Livepeer in layers, from on-chain protocol to applications.

export const CustomCardTitle = ({icon, title, variant = "card", iconSize, style = {}, className = "", ...rest}) => {
  const variants = {
    card: {
      display: 'flex',
      alignItems: 'center',
      gap: "var(--lp-spacing-2)",
      marginBottom: "var(--lp-spacing-3)",
      color: 'var(--lp-color-text-primary)',
      fontSize: '1rem',
      fontWeight: 600
    },
    accordion: {
      display: 'inline-flex',
      alignItems: 'center',
      gap: "var(--lp-spacing-2)"
    },
    tab: {
      display: 'inline-flex',
      alignItems: 'center',
      gap: '0.4rem',
      fontSize: '0.875rem'
    }
  };
  const sizes = {
    card: 20,
    accordion: 18,
    tab: 14
  };
  const size = iconSize || sizes[variant] || 20;
  const baseStyle = variants[variant] || variants.card;
  return variant === 'card' ? <div className={className} style={{
    ...baseStyle,
    ...style
  }} {...rest}>
      {typeof icon === 'string' ? <Icon icon={icon} size={size} color="var(--lp-color-accent)" /> : icon}
      {title}
    </div> : <span className={className} style={{
    ...baseStyle,
    ...style
  }} {...rest}>
      {typeof icon === 'string' ? <Icon icon={icon} size={size} color="var(--lp-color-accent)" /> : icon}
      {title}
    </span>;
};

export const ScrollableDiagram = ({children, title = '', maxHeight = '500px', minWidth = '100%', showControls = false, className = '', style = {}, ...rest}) => {
  const buildDiagramKey = (currentTitle = '', currentClassName = '') => {
    const source = `${currentTitle}|${currentClassName}|scrollable-diagram`;
    let hash = 0;
    for (let index = 0; index < source.length; index += 1) {
      hash = hash * 31 + source.charCodeAt(index) >>> 0;
    }
    return `docs-diagram-${hash.toString(36)}`;
  };
  const diagramKey = buildDiagramKey(title, className);
  const zoomName = `${diagramKey}-zoom`;
  const zoomLevels = [{
    label: '75%',
    value: 0.75
  }, {
    label: '100%',
    value: 1
  }, {
    label: '125%',
    value: 1.25
  }, {
    label: '150%',
    value: 1.5
  }];
  const containerStyle = {
    overflow: 'auto',
    maxHeight,
    border: '1px solid var(--lp-color-border-default)',
    borderRadius: "8px",
    padding: "var(--lp-spacing-4)",
    background: 'var(--lp-color-bg-card)',
    position: 'relative'
  };
  return <div className={className} style={{
    position: 'relative',
    marginBottom: "var(--lp-spacing-4)",
    ...style
  }} {...rest}>
      {title && <p style={{
    textAlign: 'center',
    fontStyle: 'italic',
    color: 'var(--lp-color-text-secondary)',
    marginBottom: "var(--lp-spacing-2)",
    fontSize: '0.875rem'
  }}>
          {title}
        </p>}

      {showControls ? <style>{`
          [data-docs-diagram-key="${diagramKey}"] [data-docs-diagram-content] {
            transform: scale(1);
            transform-origin: top left;
            width: max-content;
          }
          ${zoomLevels.map(zoomLevel => `
          #${diagramKey}-${zoomLevel.label.replace('%', '')}:checked ~ [data-docs-diagram-shell] [data-docs-diagram-content] {
            transform: scale(${zoomLevel.value});
          }
          #${diagramKey}-${zoomLevel.label.replace('%', '')}:checked ~ [data-docs-diagram-controls] label[for="${diagramKey}-${zoomLevel.label.replace('%', '')}"] {
            background: var(--lp-color-accent);
            color: var(--lp-color-on-accent);
            border-color: var(--lp-color-accent);
          }`).join('\n')}
        `}</style> : null}

      {showControls ? zoomLevels.map(zoomLevel => {
    const inputId = `${diagramKey}-${zoomLevel.label.replace('%', '')}`;
    return <input key={inputId} id={inputId} type="radio" name={zoomName} defaultChecked={zoomLevel.value === 1} style={{
      position: 'absolute',
      opacity: 0,
      pointerEvents: 'none'
    }} />;
  }) : null}

      <div data-docs-diagram-key={diagramKey} data-docs-diagram-shell style={containerStyle}>
        <div data-docs-diagram-content style={{
    minWidth,
    transformOrigin: 'top left',
    width: 'max-content'
  }}>
          {children}
        </div>
      </div>

      {showControls ? <div data-docs-diagram-controls style={{
    display: 'flex',
    justifyContent: 'flex-end',
    alignItems: 'center',
    gap: "var(--lp-spacing-2)",
    marginTop: "var(--lp-spacing-2)",
    flexWrap: 'wrap'
  }}>
          <span style={{
    fontSize: "0.75rem",
    color: 'var(--lp-color-text-muted)',
    marginRight: 'auto'
  }}>
            Scroll to pan
          </span>
          {zoomLevels.map(zoomLevel => {
    const inputId = `${diagramKey}-${zoomLevel.label.replace('%', '')}`;
    return <label key={inputId} htmlFor={inputId} style={{
      background: 'transparent',
      color: 'var(--lp-color-text-secondary)',
      border: '1px solid var(--lp-color-border-default)',
      borderRadius: "4px",
      padding: '4px 10px',
      cursor: 'pointer',
      fontSize: "0.75rem",
      fontWeight: '600'
    }}>
                {zoomLevel.label}
              </label>;
  })}
        </div> : null}
    </div>;
};

export const LinkArrow = ({href, label, description, newline = true, borderColor, className = '', style = {}, ...rest}) => {
  const linkArrowStyle = {
    display: 'inline-flex',
    alignItems: 'center',
    justifyContent: 'center',
    gap: "var(--lp-spacing-1)",
    width: 'fit-content',
    ...borderColor && ({
      borderColor
    })
  };
  return <span className={className} style={style} {...rest}>
      {newline && <br />}
      <span style={linkArrowStyle}>
        <a href={href} target="_blank" rel="noopener noreferrer">
          {label}
        </a>
        <Icon icon="arrow-up-right" size={14} color="var(--lp-color-accent)" />
      </span>
      {description && description}
      {description && <div style={{
    height: "var(--lp-spacing-3)"
  }} />}
    </span>;
};

export const layerBadges = {
  protocol: {
    color: 'green',
    label: 'Protocol'
  },
  network: {
    color: 'blue',
    label: 'Network'
  },
  orchestrator: {
    color: 'blue',
    label: 'Orchestrators'
  },
  gateway: {
    color: 'purple',
    label: 'Gateways'
  },
  platform: {
    color: 'orange',
    label: 'Platforms'
  },
  application: {
    color: 'red',
    label: 'Applications'
  },
  user: {
    color: 'gray',
    label: 'End Users'
  },
  solution: {
    color: 'yellow',
    label: 'Solutions'
  }
};

export const BadgeWrapper = ({badges, children, gap = '0.4rem', margin = '0.5rem 0 1.5rem 0', style = {}, className = '', ...rest}) => {
  const defaultStyle = {
    display: 'flex',
    flexWrap: 'wrap',
    alignItems: 'center',
    gap,
    margin
  };
  return <div className={className} style={{
    ...defaultStyle,
    ...style
  }} {...rest}>
      {badges ? badges.map((b, i) => <Badge key={i} color={b.color}>
              {b.label}
            </Badge>) : children}
    </div>;
};

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 BorderedBox = ({children, variant = "default", padding = "var(--lp-spacing-4)", borderRadius = "var(--lp-spacing-px-8)", margin = "", accentBar = "", style = {}, className = "", ...rest}) => {
  const variants = {
    default: {
      border: "1px solid var(--lp-color-border-default)",
      backgroundColor: "var(--lp-color-bg-card)"
    },
    accent: {
      border: "1px solid var(--lp-color-accent)",
      backgroundColor: "var(--lp-color-bg-card)"
    },
    muted: {
      border: "1px solid var(--lp-color-border-default)",
      backgroundColor: "transparent"
    }
  };
  const accentBarColors = {
    accent: "var(--lp-color-accent)",
    positive: "var(--green-9)"
  };
  return <div data-docs-bordered-box="" data-accent-bar={accentBarColors[accentBar] ? "" : undefined} className={className} style={{
    ...variants[variant],
    padding: padding,
    borderRadius: borderRadius,
    ...margin ? {
      margin
    } : {},
    ...accentBarColors[accentBar] ? {
      position: "relative",
      '--accent-bar-color': accentBarColors[accentBar]
    } : {},
    ...style
  }} {...rest}>
      {children}
    </div>;
};

export const Quote = ({children, className = "", style = {}, ...rest}) => {
  const quoteStyle = {
    fontSize: "1rem",
    textAlign: 'center',
    opacity: 1,
    fontStyle: 'italic',
    color: 'var(--lp-color-accent)',
    border: '1px solid var(--lp-color-border-default)',
    borderRadius: "8px",
    padding: "var(--lp-spacing-4)",
    margin: '1rem 0',
    ...style
  };
  return <blockquote className={className} style={quoteStyle} {...rest}>{children}</blockquote>;
};

export const QuadGrid = ({children, icon = "arrows-spin", iconSize = 50, iconColor = "var(--lp-color-accent)", iconBackgroundColor = "transparent", gap, spinDuration = "10s", className = "", style = {}, ...rest}) => {
  if (children == null) {
    console.warn("[QuadGrid] Missing children");
    return null;
  }
  return <div className={className} style={{
    position: "relative",
    ...style
  }} {...rest}>
      <style>{`
        @keyframes quadGridSpin {
          from { transform: rotate(0deg); }
          to { transform: rotate(360deg); }
        }
        .lp-quad-grid-layout {
          display: grid;
          grid-template-columns: repeat(2, minmax(0, 1fr));
          grid-auto-rows: 1fr;
          gap: var(--lp-quad-grid-gap, 0);
        }
        @media (max-width: 768px) {
          .lp-quad-grid-layout {
            grid-template-columns: 1fr;
          }
        }
        @media (prefers-reduced-motion: reduce) {
          .lp-quad-grid-icon {
            animation: none !important;
          }
        }
      `}</style>
      <div className="lp-quad-grid-layout" style={{
    "--lp-quad-grid-gap": gap
  }}>
        {children}
      </div>
      <div style={{
    position: "absolute",
    top: "50%",
    left: "50%",
    transform: "translate(-50%, -50%)",
    zIndex: 10
  }}>
        <div className="lp-quad-grid-icon" style={{
    backgroundColor: iconBackgroundColor,
    borderRadius: "50%",
    padding: "var(--lp-spacing-2)",
    animation: `quadGridSpin ${spinDuration} linear infinite`
  }}>
          <Icon icon={icon} size={iconSize} color={iconColor} />
        </div>
      </div>
    </div>;
};

export const DynamicTableV2 = ({tableTitle = null, headerList = [], itemsList = [], monospaceColumns = [], columnWidths = {}, columnConfig = {}, showSeparators = false, margin, className = '', style = {}, ...rest}) => {
  if (!headerList.length) {
    return <div>No headers provided</div>;
  }
  const tableRef = useRef(null);
  const [measuredColumnWidths, setMeasuredColumnWidths] = useState({});
  const measureFitColumns = () => {
    const tableElement = tableRef.current;
    if (!tableElement) {
      return;
    }
    const nextWidths = headerList.reduce((accumulator, header, index) => {
      const config = columnConfig?.[header] || ({});
      if (!config.fitContent) {
        return accumulator;
      }
      const contentNodes = tableElement.querySelectorAll(`[data-docs-column-key="${index}"] [data-docs-fit-content]`);
      let maxContentWidth = 0;
      contentNodes.forEach(node => {
        const width = Math.ceil(node.getBoundingClientRect().width);
        if (width > maxContentWidth) {
          maxContentWidth = width;
        }
      });
      if (maxContentWidth > 0) {
        accumulator[header] = `${maxContentWidth + 16}px`;
      }
      return accumulator;
    }, {});
    setMeasuredColumnWidths(currentWidths => {
      const currentEntries = Object.entries(currentWidths);
      const nextEntries = Object.entries(nextWidths);
      if (currentEntries.length === nextEntries.length && nextEntries.every(([header, width]) => currentWidths[header] === width)) {
        return currentWidths;
      }
      return nextWidths;
    });
  };
  useLayoutEffect(() => {
    measureFitColumns();
  }, [headerList, itemsList, columnConfig]);
  useEffect(() => {
    const tableElement = tableRef.current;
    if (!tableElement || typeof ResizeObserver === 'undefined') {
      return undefined;
    }
    const resizeObserver = new ResizeObserver(() => {
      measureFitColumns();
    });
    resizeObserver.observe(tableElement);
    if (tableElement.parentElement) {
      resizeObserver.observe(tableElement.parentElement);
    }
    return () => {
      resizeObserver.disconnect();
    };
  }, [headerList, itemsList, columnConfig]);
  const fitHeaders = headerList.filter(header => columnConfig?.[header]?.fitContent);
  const hasMeasuredFitColumns = fitHeaders.length === 0 || fitHeaders.every(header => Boolean(measuredColumnWidths[header]));
  const getColumnStyle = (header, isMonospace = false) => {
    const config = columnConfig?.[header] || ({});
    const fitContent = Boolean(config.fitContent);
    const fluid = Boolean(config.fluid);
    const nowrap = Boolean(config.nowrap) || fitContent || isMonospace;
    const preferredWidth = columnWidths[header];
    const measuredWidth = measuredColumnWidths[header];
    return {
      ...fitContent && measuredWidth ? {
        width: measuredWidth,
        minWidth: measuredWidth,
        maxWidth: measuredWidth
      } : {},
      ...!fitContent && !fluid && preferredWidth ? {
        minWidth: preferredWidth
      } : {},
      ...nowrap ? {
        whiteSpace: 'nowrap'
      } : {
        wordWrap: 'break-word',
        overflowWrap: 'break-word'
      }
    };
  };
  const getColumnTrackStyle = header => {
    const config = columnConfig?.[header] || ({});
    const fitContent = Boolean(config.fitContent);
    const fluid = Boolean(config.fluid);
    const preferredWidth = columnWidths[header];
    const measuredWidth = measuredColumnWidths[header];
    if (fitContent && measuredWidth) {
      return {
        width: measuredWidth,
        minWidth: measuredWidth,
        maxWidth: measuredWidth
      };
    }
    if (fluid) {
      return {};
    }
    if (preferredWidth) {
      return {
        width: preferredWidth
      };
    }
    return {};
  };
  const renderCellContent = (header, content) => {
    const config = columnConfig?.[header] || ({});
    if (!config.fitContent) {
      return content;
    }
    return <div data-docs-fit-content style={{
      display: 'inline-flex',
      alignItems: 'center',
      whiteSpace: 'nowrap',
      width: 'max-content',
      maxWidth: 'none'
    }}>
        {content}
      </div>;
  };
  return <div className={className} style={style} {...rest}>
      {tableTitle && <div style={{
    fontStyle: 'italic',
    margin: 0
  }}>
          <strong>{tableTitle}</strong>
        </div>}
      <div style={{
    overflowX: 'auto',
    ...margin != null && ({
      margin
    })
  }} role="region" tabIndex={0} aria-label={tableTitle ? `Scrollable table: ${tableTitle}` : 'Scrollable table'}>
        <table ref={tableRef} data-docs-dynamic-table-v2 style={{
    width: '100%',
    tableLayout: hasMeasuredFitColumns ? 'fixed' : 'auto',
    borderCollapse: 'collapse',
    fontSize: '0.9rem',
    marginTop: 0
  }}>
          <colgroup>
            {headerList.map((header, index) => <col key={index} style={getColumnTrackStyle(header)} />)}
          </colgroup>
          <thead>
            <tr style={{
    backgroundColor: 'var(--lp-color-accent)',
    color: 'var(--lp-color-on-accent)',
    borderBottom: '1px solid var(--lp-color-border-default)'
  }}>
              {headerList.map((header, index) => <th key={index} data-docs-column-key={index} style={{
    padding: '10px 8px',
    textAlign: 'left',
    fontWeight: '600',
    color: 'var(--lp-color-on-accent)',
    verticalAlign: 'top',
    ...getColumnStyle(header)
  }}>
                  {renderCellContent(header, header)}
                </th>)}
            </tr>
          </thead>
          <tbody>
            {itemsList.filter(item => showSeparators || !item?.__separator).map((item, rowIndex) => item?.__separator ? <tr key={rowIndex} style={{
    backgroundColor: 'var(--lp-color-accent)',
    color: 'var(--lp-color-on-accent)',
    borderBottom: '1px solid var(--lp-color-accent)'
  }}>
                    <td colSpan={headerList.length} style={{
    padding: '6px 8px',
    fontWeight: '700',
    color: 'var(--lp-color-on-accent)',
    letterSpacing: '0.01em'
  }}>
                      {(item[headerList[0]] ?? item.Category) ?? 'Category'}
                    </td>
                  </tr> : <tr key={rowIndex} style={{
    borderBottom: '1px solid var(--lp-color-border-default)'
  }}>
                    {headerList.map((header, colIndex) => {
    const value = (item[header] ?? item[header.toLowerCase()]) ?? '-';
    const isMonospace = monospaceColumns.includes(colIndex);
    return <td key={colIndex} data-docs-column-key={colIndex} style={{
      padding: '8px 8px',
      fontFamily: isMonospace ? 'monospace' : 'inherit',
      verticalAlign: 'top',
      ...getColumnStyle(header, isMonospace)
    }}>
                          {renderCellContent(header, isMonospace ? <code>{value}</code> : value)}
                        </td>;
  })}
                  </tr>)}
          </tbody>
        </table>
      </div>
    </div>;
};

export const Image = ({src, alt = "", caption, icon, hint, fullwidth = true, className = "", style = {}, ...rest}) => {
  icon = icon ? icon : "arrow-turn-down-right";
  return <Frame caption={caption} hint={hint} className={className} style={style} {...rest}>
      <img src={src} alt={alt} style={{
    width: fullwidth ? "100%" : undefined
  }} />
    </Frame>;
};

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>;
};

{/* OPENING PARAGRAPH: the stack as a frame. Bottom layer is the protocol (immutable rules); top layer is applications (Studio, Daydream, partners). Everything between is how the rules become work. */}

{/* ## Livepeer Infrastructure Layers */}

Livepeer is a decentralised serverless GPU fabric with a cryptoeconomic control plane, where services are exposed through a set of developer-friendly products and applications, enabling real-time compute infrastructure.

<QuadGrid gap="var(--lp-spacing-6)">
  <BorderedBox variant="accent">
    **Protocol**

    <BadgeWrapper badges={[layerBadges.protocol]} />

    The **Protocol** provides trust, coordination and payment mechanisms.
    Using crypto‑economic primitives, the protocol implements the incentives, staking, governance, and payments rails of the decentralised network.

    <br />

    <LinkArrow href="/v2/about/protocol/design" label="Livepeer Protocol" />
  </BorderedBox>

  <BorderedBox variant="accent">
    **Network**

    <BadgeWrapper badges={[layerBadges.orchestrator, layerBadges.gateway]} />

    The **Network** supplies compute, job routing, and verification.
    Orchestrators provide a global GPU network that performs the actual compute work, while Gateways handle job routing and verification.

    <br />

    <LinkArrow href="/v2/about/network/design" label="Livepeer Network" />
  </BorderedBox>

  <BorderedBox variant="accent">
    **Platforms**

    <BadgeWrapper badges={[layerBadges.gateway, layerBadges.platform]} />

    **Platforms** provide a set of developer‑facing tools and platforms (APIs, SDKs, platforms) which expose the network's capabilities for developers to build on and integrate with, without requiring deep knowledge of the underlying infrastructure.

    <br />

    <LinkArrow href="/v2/developers/" label="Livepeer for Developers" />
  </BorderedBox>

  <BorderedBox variant="accent">
    **Applications**

    <BadgeWrapper badges={[layerBadges.application, layerBadges.solution]} />

    **Applications** are the products and solutions built on top of the network. They are the end-user experiences that leverage the underlying infrastructure to provide AI and video capabilities for user products.

    <br />

    <br />

    <LinkArrow href="/v2/solutions/" label="Livepeer Solutions" />
  </BorderedBox>
</QuadGrid>

<br />

***Protocol vs Network vs Platform***

<DynamicTableV2
  headerList={['Concern', 'Lives Where', 'Examples']}
  itemsList={[
{
  Concern: 'Security & incentives',
  'Lives Where': 'Protocol',
  Examples: 'LPT, staking, inflation',
},
{
  Concern: 'Performance & routing',
  'Lives Where': 'Network',
  Examples: 'Orchestrators, fees',
},
{
  Concern: 'UX & onboarding',
  'Lives Where': 'Platform',
  Examples: 'APIs, dashboards',
},
{
  Concern: 'Creativity & apps',
  'Lives Where': 'Application',
  Examples: 'Daydream, studios',
},
]}
/>

The **protocol** provides trust, coordination and payment mechanisms, the **network** supplies compute, routing, and verification, and **platforms** expose the network's capabilities in a usable way.

## Infrastructure Layers

Livepeer is a decentralised serverless GPU fabric with a cryptoeconomic control plane, where services are exposed through a set of developer-friendly products and applications, enabling real-time compute infrastructure.

Livepeer's crypto-economic primitives and decentralised compute mesh provide additional benefits to the system such as censorship resistance, economic security, and trustless coordination.

<DynamicTableV2
  headerList={["Layer", "Role"]}
  itemsList={[
{ "Layer": "Client Layer", "Role": "Streams or submits jobs via Application Layer (eg. CLI, SDK, API)" },
{ "Layer": "Gateway Layer", "Role": "Receives jobs, initiates session routing" },
{ "Layer": "Orchestrator Layer", "Role": "Bids on sessions, runs compute nodes" },
{ "Layer": "Worker Layer", "Role": "Performs transcoding / AI inference" },
{ "Layer": "Off-chain Manager", "Role": "Handles bonding sync, ticket validation" },
{ "Layer": "L2 Contracts (ARB)", "Role": "TicketBroker, BondingManager, Delegator claims, reward withdrawal" },
{ "Layer": "L1 Contracts (ETH)", "Role": "LivepeerToken, BridgeMinter - token issuance & bridging only" },
]}
/>

<Card title={<CustomCardTitle title="See Livepeer as an OSI Stack" icon="file-code"/>} href="/v2/developers/" arrow />

## Livepeer Protocol and Network Architecture

Livepeer's crypto-economic primitives and decentralised compute mesh provide additional benefits to the system such as censorship resistance, economic security, and trustless coordination.

<Image src="/snippets/assets/media/diagrams/livepeer_network_protocol_layperson.svg" alt="Livepeer Network Protocol Layperson" caption="Livepeer Protocol and Network Layers" />

<br />

<Image src="/snippets/assets/media/diagrams/livepeer_network_protocol_technical.svg" alt="Livepeer Network Protocol Technical" caption="Livepeer Protocol and Network Architecture" />

### Protocol contracts

The Livepeer Protocol is a set of Solidity contracts deployed to Arbitrum One. Five contracts carry the load: `BondingManager` tracks stake and delegation, `TicketBroker` issues and redeems probabilistic micropayment tickets, `RoundsManager` advances the protocol clock, `Minter` issues LPT inflation, and `Controller` is the upgrade authority that registers all the others.

<ScrollableDiagram title="Protocol Contracts on Arbitrum One" maxHeight="600px">
  ```mermaid theme={"theme":{"light":"github-light","dark":"dark-plus"}}
  %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#18794E', 'primaryTextColor': '#fff', 'primaryBorderColor': '#3CB540', 'lineColor': '#3CB540', 'mainBkg': '#18794E', 'nodeBorder': '#3CB540', 'clusterBkg': 'transparent', 'clusterBorder': '#3CB540', 'titleColor': '#3CB540', 'edgeLabelBackground': 'transparent', 'textColor': '#3CB540', 'nodeTextColor': '#fff'}}}%%
  flowchart TB
      classDef default fill:#1a1a1a,color:#E0E4E0,stroke:#2b9a66,stroke-width:2px
      classDef proto fill:#1a1a1a,color:#E0E4E0,stroke:#f59e0b,stroke-width:2px
      classDef token fill:#1a1a1a,color:#E0E4E0,stroke:#a855f7,stroke-width:2px

      Ctrl["Controller<br/><i>upgrade authority,<br/>contract registry</i>"]:::proto

      subgraph Core["Core protocol"]
          BondMgr["BondingManager<br/><i>stake, delegation,<br/>active set, reward cut</i>"]:::proto
          TicketB["TicketBroker<br/><i>probabilistic micropayments,<br/>winning ticket redemption</i>"]:::proto
          Rounds["RoundsManager<br/><i>round transitions,<br/>protocol clock</i>"]:::proto
          Minter["Minter<br/><i>LPT inflation,<br/>treasury distribution</i>"]:::proto
      end

      subgraph Tokens["Tokens & treasury"]
          LPT["LivepeerToken (LPT)<br/><i>governance, staking</i>"]:::token
          Treasury["Treasury<br/><i>protocol-owned LPT</i>"]:::token
      end

      Ctrl -->|registers + upgrades| BondMgr
      Ctrl -->|registers + upgrades| TicketB
      Ctrl -->|registers + upgrades| Rounds
      Ctrl -->|registers + upgrades| Minter
      Minter -->|mints| LPT
      Minter -->|funds| Treasury
      BondMgr -->|reads stake| LPT
      TicketB -->|reads active set| BondMgr
      BondMgr -->|advances on| Rounds
  ```
</ScrollableDiagram>

These contracts run unchanged across every node in the network. An Orchestrator earns by redeeming winning tickets at `TicketBroker` and reward calls at `BondingManager`; a Delegator earns by bonding LPT through `BondingManager`. See [Protocol Architecture](/v2/about/protocol/architecture) for contract addresses and ABIs.

<Card title={<CustomCardTitle title="See full contract details" icon="file-code"/>} href="/v2/developers/protocol/architecture" arrow />

### Network nodes

The network layer is a single binary, `go-livepeer`, run in different modes. One mode is the Gateway: it accepts video and AI jobs from clients, selects an Orchestrator, and settles payment in tickets. Another is the Orchestrator: it advertises capabilities, receives jobs, runs them, and redeems winning tickets on-chain. The transcoder mode is a worker that an Orchestrator can split off onto a separate machine to scale horizontally. Newer modes – redeemer and remote signer – separate ticket redemption and key custody from the live job path so that Gateway implementations in other languages can integrate.

<ScrollableDiagram title="go-livepeer Node Modes" maxHeight="600px">
  ```mermaid theme={"theme":{"light":"github-light","dark":"dark-plus"}}
  %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#18794E', 'primaryTextColor': '#fff', 'primaryBorderColor': '#3CB540', 'lineColor': '#3CB540', 'mainBkg': '#18794E', 'nodeBorder': '#3CB540', 'clusterBkg': 'transparent', 'clusterBorder': '#3CB540', 'titleColor': '#3CB540', 'edgeLabelBackground': 'transparent', 'textColor': '#3CB540', 'nodeTextColor': '#fff'}}}%%
  flowchart LR
      classDef default fill:#1a1a1a,color:#E0E4E0,stroke:#2b9a66,stroke-width:2px
      classDef gw fill:#1a1a1a,color:#E0E4E0,stroke:#3b82f6,stroke-width:2px
      classDef orch fill:#1a1a1a,color:#E0E4E0,stroke:#2b9a66,stroke-width:2px
      classDef aux fill:#1a1a1a,color:#E0E4E0,stroke:#a855f7,stroke-width:2px

      Client["Client app<br/>(Studio, Daydream, SDK)"]:::default

      subgraph DemandSide["Demand side"]
          Gateway["Gateway<br/>job routing, payment,<br/>session management"]:::gw
          RemoteSigner["Remote Signer<br/>key custody,<br/>ticket signing"]:::aux
          Redeemer["Redeemer<br/>ticket redemption"]:::aux
      end

      subgraph SupplySide["Supply side"]
          Orchestrator["Orchestrator<br/>capability advertisement,<br/>job acceptance, payment receipt"]:::orch
          Transcoder["Transcoder<br/>video transcoding worker"]:::orch
          AIWorker["AI Worker<br/>AI inference worker"]:::orch
      end

      Client -->|"video/AI job"| Gateway
      Gateway -->|"signs ticket"| RemoteSigner
      Gateway -->|"forwards ticket"| Redeemer
      Gateway -->|"job + ticket"| Orchestrator
      Orchestrator -->|"transcode work"| Transcoder
      Orchestrator -->|"AI work"| AIWorker
  ```
</ScrollableDiagram>

A small operator runs a single binary that fills both Gateway and Orchestrator roles. A larger operator splits the modes onto separate machines: Orchestrator on the network edge, transcoders or AI workers behind it on a private subnet. See [Network Architecture](/v2/network/architecture) for the deployment topology.

### Off-chain coordination

Most of what happens on the network never touches a contract. Gateways discover Orchestrators through the on-chain subgraph, direct configuration, a webhook, or the Network Capabilities API. Orchestrators advertise capabilities and prices in `OrchestratorInfo` messages. Payment runs in probabilistic micropayment tickets that batch off-chain until a winning ticket triggers an on-chain redemption. This off-chain layer is what makes per-frame, per-pixel pricing economical.

<ScrollableDiagram title="Off-Chain Coordination Loop" maxHeight="600px">
  ```mermaid theme={"theme":{"light":"github-light","dark":"dark-plus"}}
  %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#18794E', 'primaryTextColor': '#fff', 'primaryBorderColor': '#3CB540', 'lineColor': '#3CB540', 'mainBkg': '#18794E', 'nodeBorder': '#3CB540', 'clusterBkg': 'transparent', 'clusterBorder': '#3CB540', 'titleColor': '#3CB540', 'edgeLabelBackground': 'transparent', 'textColor': '#3CB540', 'nodeTextColor': '#fff'}}}%%
  sequenceDiagram
      autonumber
      participant G as Gateway
      participant Sub as Arbitrum subgraph
      participant O as Orchestrator
      participant TB as TicketBroker (on-chain)

      G->>Sub: query active orchestrators<br/>(stake, service URI)
      Sub-->>G: candidate list
      G->>O: GetOrchestrator request
      O-->>G: OrchestratorInfo<br/>(capabilities, price,<br/>ticket params)
      G->>G: filter by price + capability<br/>+ blacklist + score
      G->>O: job + probabilistic ticket
      O->>O: process job, accumulate tickets
      O-->>G: result
      Note over O,TB: only winning tickets<br/>(low probability per ticket)
      O->>TB: redeem winning ticket
      TB-->>O: settle ETH from gateway deposit
  ```
</ScrollableDiagram>

The off-chain loop is what scales the network: thousands of jobs and tickets per second between Gateway and Orchestrator, with on-chain settlement only when a ticket wins. See [Marketplace and Discovery](/v2/network/marketplace) for the full discovery and selection algorithm.

### AI Runtime

The AI runtime sits inside the Orchestrator. `ai-worker` is a Go subsystem in `go-livepeer` that owns the Orchestrator-side job lifecycle: it receives an AI job from the Gateway, picks a registered pipeline, starts or wakes the corresponding container, and streams frames in and out. Each pipeline runs as a separate `ai-runner` Python container, isolated by GPU and model. The transport between `ai-worker` and `ai-runner` for real-time work is the trickle protocol; for batch work it is a request/response HTTP API. ComfyStream is one such container, offering a ComfyUI-graph runtime for real-time video-to-video pipelines.

<ScrollableDiagram title="AI Runtime – ai-worker and ai-runner" maxHeight="600px">
  ```mermaid theme={"theme":{"light":"github-light","dark":"dark-plus"}}
  %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#18794E', 'primaryTextColor': '#fff', 'primaryBorderColor': '#3CB540', 'lineColor': '#3CB540', 'mainBkg': '#18794E', 'nodeBorder': '#3CB540', 'clusterBkg': 'transparent', 'clusterBorder': '#3CB540', 'titleColor': '#3CB540', 'edgeLabelBackground': 'transparent', 'textColor': '#3CB540', 'nodeTextColor': '#fff'}}}%%
  flowchart TB
      classDef default fill:#1a1a1a,color:#E0E4E0,stroke:#2b9a66,stroke-width:2px
      classDef goproc fill:#1a1a1a,color:#E0E4E0,stroke:#2b9a66,stroke-width:2px
      classDef py fill:#1a1a1a,color:#E0E4E0,stroke:#a855f7,stroke-width:2px
      classDef cfg fill:#1a1a1a,color:#E0E4E0,stroke:#f59e0b,stroke-width:2px

      Gateway["Gateway<br/>(remote)"]:::default

      subgraph Orchestrator["Orchestrator host"]
          Orch["go-livepeer<br/>orchestrator process"]:::goproc
          AIWorker["ai-worker subsystem<br/>job dispatch, container<br/>lifecycle, payment"]:::goproc

          subgraph Containers["ai-runner containers"]
              Batch["Batch pipelines<br/>text-to-image,<br/>image-to-video,<br/>audio-to-text…"]:::py
              Realtime["Real-time pipelines<br/>live-video-to-video,<br/>ComfyStream"]:::py
          end

          AIModels["aiModels.json<br/><i>configured pipelines<br/>+ pricing</i>"]:::cfg
      end

      Gateway -->|AI job| Orch
      Orch -->|dispatch| AIWorker
      AIWorker -->|HTTP request/response| Batch
      AIWorker -->|trickle protocol<br/>WebRTC frames| Realtime
      AIModels -->|loaded at start| AIWorker
  ```
</ScrollableDiagram>

An Orchestrator declares which pipelines it serves through `aiModels.json`, which sets per-pipeline pricing and warm-model strategy. See [AI Capabilities](/v2/network/capabilities) for the pipeline catalogue and pricing units.

### Applications and integrations

Applications are the products built on the layers below. Livepeer Studio is the hosted video product run by Livepeer Inc – streaming, VOD, transcoding, and the embeddable player behind an API key. Daydream is the hosted real-time AI video product, built on ComfyStream and the AI Gateway API. Storyboard is an AI agent workspace that uses Daydream as its inference backend. Streamplace is the live-video product for the AT Protocol and Bluesky. BYOC ("bring your own container") is the path for a partner to plug a custom AI pipeline into the network as an Orchestrator-side container.

<ScrollableDiagram title="Applications and Their Layer Position" maxHeight="600px">
  ```mermaid theme={"theme":{"light":"github-light","dark":"dark-plus"}}
  %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#18794E', 'primaryTextColor': '#fff', 'primaryBorderColor': '#3CB540', 'lineColor': '#3CB540', 'mainBkg': '#18794E', 'nodeBorder': '#3CB540', 'clusterBkg': 'transparent', 'clusterBorder': '#3CB540', 'titleColor': '#3CB540', 'edgeLabelBackground': 'transparent', 'textColor': '#3CB540', 'nodeTextColor': '#fff'}}}%%
  flowchart TB
      classDef default fill:#1a1a1a,color:#E0E4E0,stroke:#2b9a66,stroke-width:2px
      classDef firstparty fill:#1a1a1a,color:#E0E4E0,stroke:#a855f7,stroke-width:2px
      classDef partner fill:#1a1a1a,color:#E0E4E0,stroke:#3b82f6,stroke-width:2px
      classDef platform fill:#1a1a1a,color:#E0E4E0,stroke:#f59e0b,stroke-width:2px

      subgraph FirstParty["First-party apps (Livepeer Inc)"]
          Studio["Livepeer Studio<br/>hosted video"]:::firstparty
          Daydream["Daydream<br/>hosted real-time AI video"]:::firstparty
      end

      subgraph Partner["Partner & community apps"]
          Storyboard["Storyboard<br/>AI agent workspace"]:::partner
          Streamplace["Streamplace<br/>live video on AT Protocol"]:::partner
          BYOC["BYOC pipelines<br/>custom containers"]:::partner
      end

      subgraph Platforms["Platform layer (Layer 3)"]
          StudioAPI["Studio API"]:::platform
          AIGwAPI["AI Gateway API"]:::platform
          ComfyStream["ComfyStream"]:::platform
      end

      Studio --> StudioAPI
      Daydream --> AIGwAPI
      Daydream --> ComfyStream
      Storyboard --> AIGwAPI
      Streamplace --> StudioAPI
      BYOC --> AIGwAPI
  ```
</ScrollableDiagram>

A reader choosing where to build picks the highest layer that meets their needs. Studio for managed video, Daydream for managed AI video, the AI Gateway API for direct network calls, ComfyStream for custom real-time pipelines, BYOC for new pipeline types. See [Solutions](/v2/solutions) for the full product catalogue.

## How the layers interact

A job is the test: it touches every layer in one round trip. The job descends from an application call to platform routing to network compute, then settles back up in payment to the operator and a small redemption fee on-chain. Governance acts across, not through, the live job path.

<ScrollableDiagram title="One Job Through the Stack" maxHeight="700px">
  ```mermaid theme={"theme":{"light":"github-light","dark":"dark-plus"}}
  %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#18794E', 'primaryTextColor': '#fff', 'primaryBorderColor': '#3CB540', 'lineColor': '#3CB540', 'mainBkg': '#18794E', 'nodeBorder': '#3CB540', 'clusterBkg': 'transparent', 'clusterBorder': '#3CB540', 'titleColor': '#3CB540', 'edgeLabelBackground': 'transparent', 'textColor': '#3CB540', 'nodeTextColor': '#fff'}}}%%
  sequenceDiagram
      autonumber
      participant App as Layer 4<br/>Application
      participant Plat as Layer 3<br/>Platform<br/>(Studio API / AI Gateway API)
      participant Net as Layer 2<br/>Network<br/>(Gateway + Orchestrator)
      participant Proto as Layer 1<br/>Protocol<br/>(Arbitrum)

      App->>Plat: call SDK / API<br/>(video stream or AI prompt)
      Plat->>Net: forward job to gateway
      Net->>Net: gateway selects orchestrator<br/>off-chain (capabilities, price, score)
      Net->>Net: orchestrator runs work<br/>(transcode / inference)
      Net-->>Plat: result stream
      Plat-->>App: result delivered
      Note over Net: per-job: probabilistic ticket<br/>(off-chain, mostly losing)
      Net->>Proto: redeem winning ticket
      Proto-->>Net: settle ETH from gateway deposit
      Note over Proto: per-round: inflation reward<br/>distributed by stake
      Proto-->>Net: LPT inflation reward
  ```
</ScrollableDiagram>

The protocol does not see most jobs – it only sees the ones whose tickets win and the per-round inflation distribution. That is the design that makes per-pixel and per-token pricing viable on Arbitrum.

{/* It includes a protocol layer that implements the economic incentives and rules of the system, a network layer of independently operated nodes that perform the compute work, and a growing ecosystem of platforms and applications that expose the network's capabilities in usable ways. */}
