> ## Documentation Index
> Fetch the complete documentation index at: https://na-36-mintlify-aebde2c5.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Network Architecture

> The running fleet's structure, the off-chain coordination layer, the on-chain anchor surfaces, and where the Network is observable from outside.

export const BorderedBox = ({children, variant = "default", padding = "var(--lp-spacing-4)", borderRadius = "var(--lp-spacing-px-8)", 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,
    ...accentBarColors[accentBar] ? {
      position: "relative",
      '--accent-bar-color': accentBarColors[accentBar]
    } : {},
    ...style
  }} {...rest}>
      {children}
    </div>;
};

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

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 Subtitle = ({style = {}, text, children, variant = 'default', className = '', ...rest}) => {
  const variants = {
    default: {
      fontSize: '1rem',
      fontStyle: 'italic',
      color: 'var(--lp-color-accent)',
      marginBottom: 0
    },
    changelog: {
      fontSize: '0.8rem',
      fontStyle: 'normal',
      fontWeight: 700,
      color: 'var(--lp-color-text-primary)',
      marginBottom: 0
    }
  };
  const base = variants[variant] || variants.default;
  return <span className={className} style={{
    ...base,
    ...style
  }} {...rest}>
      {text}
      {children}
    </span>;
};

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

<Quote>
  The Network is a running fleet of `go-livepeer` nodes coordinating off-chain and anchored to four protocol contracts on Arbitrum One. The fleet is observable from outside through the Explorer, the subgraph, and the Network Capabilities API.
</Quote>

<CustomDivider style={{ margin: 0, marginBottom: '-2rem' }} />

## Three Architectural Layers

The Network's architecture is three layers stacked: the running fleet of nodes, the off-chain coordination layer they communicate over, and the on-chain anchor surfaces they read from and write to. Each layer answers a different question.

<DynamicTableV2
  headerList={['Layer', 'What it is', 'What it answers']}
  itemsList={[
{
  Layer: <Subtitle variant="changelog">**Node fleet**</Subtitle>,
  'What it is': 'The running `go-livepeer` processes operated by gateways, orchestrators, and their attached workers',
  'What it answers': 'What machines are running, what role each plays, how they connect to clients and to each other',
},
{
  Layer: <Subtitle variant="changelog">**Off-chain coordination**</Subtitle>,
  'What it is': 'Direct gateway-to-orchestrator traffic: capability advertisement, job dispatch, ticket exchange, real-time frame transport',
  'What it answers': 'How nodes find each other, agree on price, exchange work, and pay for it without touching the chain',
},
{
  Layer: <Subtitle variant="changelog">**On-chain anchor**</Subtitle>,
  'What it is': 'Four protocol contracts on Arbitrum One that the off-chain layer reads and writes against',
  'What it answers': 'What the Network commits to permanently, where stake and settlement live, what is publicly auditable',
},
]}
/>

<CustomDivider style={{ margin: '-1rem 0 -2rem 0' }} />

## The Running Fleet

The fleet is operated by independent participants. Orchestrators run GPU nodes and any workers attached to them. Gateways run routing and payment infrastructure. Both run the same `go-livepeer` binary in different modes. A small operator runs one process; a large operator splits the modes across separate machines on a private subnet, with the orchestrator on the network edge.

<ScrollableDiagram title="Network Fleet and the Boundary it Anchors To" maxHeight="700px">
  ```mermaid theme={"theme":{"light":"github-light","dark":"dark-plus"}}
  %%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#1a1a1a', 'primaryTextColor': '#E0E4E0', 'primaryBorderColor': '#2b9a66', 'lineColor': '#2b9a66', 'secondaryColor': '#0d0d0d', 'tertiaryColor': '#1a1a1a', 'background': '#0d0d0d', 'fontFamily': "Inter, 'Inter Fallback', -apple-system, system-ui" }}}%%
  flowchart TB
      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 chain fill:#1a1a1a,color:#E0E4E0,stroke:#f59e0b,stroke-width:2px
      classDef obs fill:#1a1a1a,color:#E0E4E0,stroke:#a855f7,stroke-width:2px

      Client["Client app"]:::default

      subgraph DemandSide["Demand-side fleet"]
          Gateway["Gateway nodes<br/>job intake, routing,<br/>payment"]:::gw
      end

      subgraph SupplySide["Supply-side fleet"]
          Orch["Orchestrator nodes<br/>capability advertisement,<br/>job dispatch"]:::orch
          Workers["Worker processes<br/>transcoders, AI workers"]:::orch
      end

      subgraph OnChain["On-chain anchor (Arbitrum One)"]
          Bond["BondingManager"]:::chain
          Broker["TicketBroker"]:::chain
          Registry["ServiceRegistry"]:::chain
          Minter["Minter"]:::chain
      end

      subgraph Outside["Outside-the-fleet observability"]
          Explorer["Livepeer Explorer"]:::obs
          Subgraph["Arbitrum subgraph"]:::obs
          CapsAPI["Network Capabilities API"]:::obs
      end

      Client -->|job| Gateway
      Gateway -->|job + ticket| Orch
      Orch -->|dispatch| Workers
      Orch -.->|stake, active set| Bond
      Gateway -.->|deposit| Broker
      Orch -.->|redeem| Broker
      Orch -.->|service URIs| Registry
      Bond -.->|round rewards| Minter

      Explorer -.-> Bond
      Explorer -.-> Broker
      Subgraph -.-> Bond
      Subgraph -.-> Registry
      CapsAPI -.-> Orch
  ```
</ScrollableDiagram>

Solid arrows are off-chain traffic at job rate. Dashed arrows from the fleet to the chain are settlement and registration, at lower frequency. Dashed arrows from the observability surfaces are read-only data flows that let outside readers see what the fleet is doing without operating any of it.

<CustomDivider style={{ margin: '-1rem 0 -2rem 0' }} />

## Off-Chain Coordination

The off-chain layer is what the Network is sized for. Discovery, capability advertisement, price agreement, job dispatch, and ticket exchange all happen here, between gateway and orchestrator, over standard transports. The chain stays out of the hot path.

Four communication patterns run continuously across the off-chain layer:

<DynamicTableV2
  headerList={['Pattern', 'Who participates', 'What flows']}
  itemsList={[
{
  Pattern: <Subtitle variant="changelog">**Capability advertisement**</Subtitle>,
  'Who participates': 'Orchestrator publishes; gateways read',
  'What flows': '`OrchestratorInfo` messages: capability set, price-per-unit, ticket parameters',
},
{
  Pattern: <Subtitle variant="changelog">**Discovery and selection**</Subtitle>,
  'Who participates': 'Gateway queries; protocol contracts and orchestrators respond',
  'What flows': 'Candidate lists from `ServiceRegistry` (often via subgraph), then per-candidate `OrchestratorInfo`',
},
{
  Pattern: <Subtitle variant="changelog">**Job dispatch and result return**</Subtitle>,
  'Who participates': 'Gateway and orchestrator',
  'What flows': 'Segments and frames in; transcoded or generated output back; tickets attached to each segment',
},
{
  Pattern: <Subtitle variant="changelog">**Real-time frame transport**</Subtitle>,
  'Who participates': 'Gateway and orchestrator AI worker',
  'What flows': 'Live video frames over the trickle protocol for sub-second AI pipelines',
},
]}
/>

<CustomDivider style={{ margin: '-1rem 0 -2rem 0' }} />

## On-Chain Anchor Surfaces

The Network reads from and writes to four contracts on Arbitrum One. These are the surfaces the off-chain fleet relies on for ground truth.

<DynamicTableV2
  headerList={['Contract', 'What the Network uses it for']}
  itemsList={[
{
  Contract: <Subtitle variant="changelog">**`BondingManager`**</Subtitle>,
  'What the Network uses it for': 'Source of truth for orchestrator stake, active-set membership, and reward-cut configuration. Read by gateways during selection; written by orchestrators when calling `reward` each round.',
},
{
  Contract: <Subtitle variant="changelog">**`TicketBroker`**</Subtitle>,
  'What the Network uses it for': 'Holds gateway deposits and reserves. Receives winning ticket redemptions. Settles ETH from the gateway side to the orchestrator side.',
},
{
  Contract: <Subtitle variant="changelog">**`ServiceRegistry`**</Subtitle>,
  'What the Network uses it for': 'Holds the service URI each orchestrator publishes. Read by gateways during discovery to know where each orchestrator is reachable.',
},
{
  Contract: <Subtitle variant="changelog">**`Minter`**</Subtitle>,
  'What the Network uses it for': 'Mints LPT inflation per round. Distributed to bonded stake when orchestrators call `reward`. The reward stream that runs in parallel to job-fee settlement.',
},
]}
/>

<Card title={<CustomCardTitle icon="file-code" title="Blockchain Contracts" />} href="/v2/about/protocol/blockchain-contracts" horizontal arrow> Full contract addresses, ABIs, and reference. </Card>

<CustomDivider style={{ margin: '-1rem 0 -2rem 0' }} />

## Observable from Outside

The Network is observable from outside the fleet through three surfaces. Researchers, evaluators, and integrators do not need to operate any node to see what the Network is doing.

<DynamicTableV2
  headerList={['Surface', 'What it exposes', 'Who uses it']}
  itemsList={[
{
  Surface: <Subtitle variant="changelog">**Livepeer Explorer**</Subtitle>,
  'What it exposes': 'Active orchestrators, bonded supply, active set, round state, treasury balance, governance proposals',
  'Who uses it': 'Delegators choosing orchestrators; researchers citing network state; operators monitoring round status',
},
{
  Surface: <Subtitle variant="changelog">**Arbitrum subgraph**</Subtitle>,
  'What it exposes': 'Indexed view of all on-chain events: stake changes, ticket redemptions, reward calls, governance votes',
  'Who uses it': 'Gateways during discovery; data products and dashboards; researchers running historical queries',
},
{
  Surface: <Subtitle variant="changelog">**Network Capabilities API**</Subtitle>,
  'What it exposes': 'Aggregated orchestrator capability data across the network: which AI pipelines, which transcoding profiles, which BYOC containers',
  'Who uses it': 'Gateways needing capability filtering richer than `ServiceRegistry` alone; integrators evaluating coverage',
},
]}
/>

The three surfaces share a property: they let an outside reader build a real picture of the Network's state without trusting any single operator. Each surface reads ground truth from the chain or from operator-published data, not from a central authority.

<CustomDivider />

## Related Pages

<Columns cols={2}>
  <Card title={<CustomCardTitle icon="circle-nodes" title="Network Design" />} href="/v2/about/network/design" horizontal arrow>
    Purpose, properties, actors.
  </Card>

  <Card title={<CustomCardTitle icon="store" title="Marketplace Model" />} href="/v2/about/network/marketplace-model" horizontal arrow>
    Market shape, settlement, verification.
  </Card>

  <Card title={<CustomCardTitle icon="plug" title="Network Interfaces" />} href="/v2/about/network/interfaces" horizontal arrow>
    Reachable surfaces and entry points.
  </Card>

  <Card title={<CustomCardTitle icon="diagram-project" title="Infrastructure Stack" />} href="/v2/about/concepts/livepeer-stack" horizontal arrow>
    The four-layer Livepeer stack.
  </Card>
</Columns>

{  /* ---
title: Network Technical Architecture
<<<<<<<< HEAD:v2/about/network/technical-architecture.mdx
sidebarTitle: Technical Architecture
========
sidebarTitle: Architecture
>>>>>>>> docs-v2-dev:v2/about/network/architecture.mdx
description: >-
Technical stack of the Livepeer Network: node types, gateway infrastructure, APIs, CLI, SDKs, and monitoring. How
orchestrators, gateways, and workers fit together.
lifecycleStage: discover
complexity: intermediate
pageType: concept
audience: general
lastVerified: 2026-03-17T00:00:00.000Z
purpose: concept
keywords:
- livepeer
- about
- livepeer network
- technical architecture
- stack
- orchestrator
- gateway
- go-livepeer
'og:image': /snippets/assets/media/og-images/en/about.png
---
import { DynamicTable } from '/snippets/components/displays/tables/Tables.jsx'
import { GotoCard, GotoLink } from '/snippets/components/elements/links/Links.jsx'

<<<<<<<< HEAD:v2/about/network/technical-architecture.mdx

========
>>>>>>>> docs-v2-dev:v2/about/network/architecture.mdx
the full stack of tools, infrastructure, and components that power the Livepeer Network at the node, Gateway, and client level. Livepeer's architecture is modular and developer-facing: you can run an Orchestrator, build a custom AI Gateway, or use APIs to build media apps on decentralised compute.

## Architecture layers

```mermaid
graph TD
  UI[Apps / SDKs / Interfaces]
  API[Gateway APIs / REST / GraphQL]
  GW[Gateway Nodes]
  O[Orchestrator Node - go-livepeer]
  W[Worker Layer - FFmpeg / AI]
  P[Protocol - Ethereum / Arbitrum]

  UI --> API --> GW --> O --> W
  O --> P
```

The network sits above the protocol: Gateways and Orchestrators handle off-chain compute and routing; the protocol (Arbitrum) handles staking, tickets, and rewards.

## Orchestrator node

The Orchestrator node runs **go-livepeer** (the `livepeer` binary), available at:

[https://github.com/livepeer/go-livepeer](https://github.com/livepeer/go-livepeer)

### Key components

- **Transcoder selection** - Internal or external workers; configured via `orchSecret` and `orchAddr` for remote transcoders
- **Ticket validation** - L2 `TicketBroker` on Arbitrum for ETH payment redemption
- **Reward claim** - Merkle submission to `BondingManager` each round
- **LPT staking** - BondingManager for self-bond and delegator stake
- **Region advertisement** - For Gateway routing (latency, capacity)

### Deployment modes

- Bare metal with GPU
- Containerized
- Cloud auto-scaling

### Tools

- **livepeer_cli** - Stake, set fee/reward cut, monitor sessions
- **livepeer_exporter** - Prometheus metrics exporter for observability

## Worker layer

Workers can be local or remote services attached to an Orchestrator:

<DynamicTable
headerList={["Type", "Language / runtime", "Example use"]}
itemsList={[
  { "Type": "Transcoder", "Language / runtime": "FFmpeg", "Example use": ".ts segment processing, multi-bitrate output" },
  { "Type": "Inference", "Language / runtime": "Python (Torch)", "Example use": "AI tasks, e.g. SDXL image-to-image" },
  { "Type": "Plugin", "Language / runtime": "WebRTC / C++", "Example use": "Real-time browser capture or object detection" }
]}
/>

Configured via Orchestrator `config.json` or environment variables.

## Gateway infrastructure

Gateways manage:

- Session auth (API key, ETH deposit, or credit check)
- Job routing to Orchestrators
- Session logging and retries

**Codebases:**

- [Studio Gateway](https://github.com/livepeer/studio-gateway)
- [Daydream Gateway](https://github.com/livepeer/daydream)
- [Cascade](https://github.com/livepeer/cascade) - Load balancer and AI workflow coordination

**Features:** Rate limiting, region scoring, health probes, fallback Orchestrators, credit tracking (e.g. Postgres/Redis).

## APIs

<DynamicTable
headerList={["API", "Protocol", "Description"]}
itemsList={[
  { "API": "REST Gateway", "Protocol": "HTTPS", "Description": "Transcode, AI job control (e.g. Livepeer Studio API)" },
  { "API": "gRPC Gateway", "Protocol": "gRPC", "Description": "Fast session handshakes, monitoring (e.g. ReserveSession, Heartbeat)" },
  { "API": "Explorer API", "Protocol": "GraphQL", "Description": "Metrics, staking, round data (explorer.livepeer.org)" }
]}
/>

**Endpoints:**

- `https://livepeer.studio/api` - Studio REST
- `https://explorer.livepeer.org/graphql` - Explorer GraphQL

## CLI and SDKs

**CLI:** `livepeer_cli` (shipped with go-livepeer)

- Stake LPT, bond/unbond
- Set Orchestrator fees and reward cut
- Watch active sessions, query protocol state

**SDKs:**

- **[Livepeer JS SDK](https://github.com/livepeer/js-sdk)** - Playback, ingest, AI session tools; works in Node.js and browser
- **Python AI pipelines** - Used in internal and community projects (e.g. DotSimulate, MetaDJ)

## Monitoring and observability

<DynamicTable
headerList={["Tool", "Metric type", "Description"]}
itemsList={[
  { "Tool": "Prometheus", "Metric type": "Session, CPU, ticketing", "Description": "Exposed via livepeer_exporter" },
  { "Tool": "Grafana dashboards", "Metric type": "Visual ops", "Description": "Studio and Orchestrator internal views" },
  { "Tool": "Loki", "Metric type": "Logs", "Description": "Transcode errors, API retries" },
  { "Tool": "Gateway logs", "Metric type": "Credits, API, routing", "Description": "Per-session logs (e.g. Redis / S3)" }
]}
/>

Node software exposes explicit metrics (e.g. Segment success rate, ticket value sent/redeemed, orchestrator swaps); see [Job lifecycle](./job-lifecycle) for event/transition details.

## Deployment examples

- [Orchestrator on AWS](https://github.com/livepeer/orchestrator-on-aws)
- [Studio gateway deploy](https://github.com/livepeer/studio-gateway-deploy)
- [Daydream AI node pipeline](https://github.com/livepeer/daydream)

## See also

- [Interfaces](./interfaces) - REST, gRPC, GraphQL, JS SDK, CLI, and smart contract access
- [Marketplace](./marketplace) - How Gateways route jobs and how pricing works
- [Job lifecycle](./job-lifecycle) - End-to-end flow and state machine
- [Protocol technical architecture](../protocol/technical-architecture) - On-chain contracts and node types from protocol perspective
- [Blockchain contracts](../protocol/blockchain-contracts) - Contract addresses (Arbitrum)

## References

- [Livepeer GitHub](https://github.com/livepeer)
- [Orchestrator docs](/v2/orchestrators/portal)
- [Daydream Gateway](https://github.com/livepeer/daydream)
- [Livepeer Explorer](https://explorer.livepeer.org) */}
