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

# Livepeer Protocol Technical Architecture

> How the Livepeer protocol is structured as a layered system of on-chain contracts on Arbitrum One, off-chain coordination protocols, and node software, and how these layers interact to secure media and AI compute work.

export const FlexContainer = ({children, direction = "row", gap = "var(--lp-spacing-4)", align = "flex-start", justify = "flex-start", wrap = false, marginTop = "", marginBottom = "", margin = "", padding = "", style = {}, className = "", ...rest}) => {
  return <div className={className} style={{
    display: "flex",
    flexDirection: direction,
    gap: gap,
    alignItems: align,
    justifyContent: justify,
    flexWrap: wrap ? "wrap" : "nowrap",
    ...margin ? {
      margin
    } : {},
    ...padding ? {
      padding
    } : {},
    ...marginTop ? {
      marginTop
    } : {},
    ...marginBottom ? {
      marginBottom
    } : {},
    ...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 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 CardTitleTextWithArrow = ({children, className = '', style = {}, ...cardProps}) => {
  return <div className={className} style={{
    display: 'flex',
    width: 'fit-content',
    alignItems: 'center',
    justifyContent: 'center',
    marginTop: '-1rem',
    ...style
  }}>
      <Card arrow={false} title={<span style={{
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center'
  }}>
            {' '}
            {children}{' '}
            <span style={{
    margin: '0 -1rem 0.2rem 0.75rem'
  }}>
              <Icon icon="arrow-up-right" size={16} color="var(--lp-color-text-secondary)" />
            </span>
          </span>} {...cardProps} />
    </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 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>;
};

<FlexContainer justify="center" padding="0" margin="0">
  <CardTitleTextWithArrow icon="github" horizontal href="https://github.com/livepeer/protocol"> Livepeer Protocol </CardTitleTextWithArrow>
</FlexContainer>

<CustomDivider style={{margin: 0, marginBottom: "-1rem"}} />

<Quote>
  The Livepeer Protocol is a layered system. Smart contracts on Arbitrum One handle staking, payments, and coordination. Off-chain protocols handle job negotiation and per-segment payment. Node software runs the actual media and AI compute work. Each layer has a defined role, and the boundaries between them are what make the network scalable and verifiable.
</Quote>

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

The protocol is structured so that on-chain contracts coordinate economic security and off-chain components handle high-throughput compute. The blockchain settles state that must be globally agreed upon - stake, payments, governance. Everything else runs off-chain and references the chain only when it has to.

<CustomDivider />

## Three Architectural Layers

The protocol stack has three layers, each with a different responsibility and a different rate of change. The blockchain layer is slow, expensive, and authoritative. The protocol layer is fast and trust-minimised. The node layer is where the actual work happens.

<DynamicTableV2
  headerList={["Layer", "Runs on", "Responsibility", "Rate of change"]}
  itemsList={[
{
  "Layer": "Blockchain",
  "Runs on": "Arbitrum One smart contracts",
  "Responsibility": "Staking, payments, round progression, service registry, governance, treasury",
  "Rate of change": "Per round (~24h), per LIP, per redemption",
},
{
  "Layer": "Protocol",
  "Runs on": "Peer-to-peer between Gateways and Orchestrators",
  "Responsibility": "Service discovery, price negotiation, per-segment payment tickets, verification",
  "Rate of change": "Per session, per segment",
},
{
  "Layer": "Node",
  "Runs on": "go-livepeer software on participant machines",
  "Responsibility": "Video transcoding, AI inference, session management, ticket validation",
  "Rate of change": "Per request, per frame",
},
]}
/>

<CustomDivider />

## Blockchain Layer

The blockchain layer is the protocol's source of truth for any state that must be globally agreed and economically secured. Smart contracts deployed on Arbitrum One coordinate stake, settle payments, govern parameters, and define the Active Set of Orchestrators eligible for work.

The contracts are organised around a central `Controller` that records the deployed address of every other protocol contract. Contracts query the Controller to find each other, which makes upgrades possible without redeploying the entire system.

<ScrollableDiagram title="Livepeer Protocol Contract Architecture" 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 TD
      classDef default stroke-width:2px
      subgraph Core["Core Protocol Contracts"]
          Controller --> ServiceRegistry
          Controller --> BondingManager
          Controller --> RoundsManager
          Controller --> TicketBroker
          Controller --> Minter
      end
      subgraph Gov["Governance"]
          BondingVotes --> LivepeerGovernor
          LivepeerGovernor --> Governor
          LivepeerGovernor --> Treasury
      end
      subgraph Token["Token System"]
          LivepeerToken
      end
      BondingManager --> BondingVotes
      Controller --> LivepeerGovernor
      Minter --> LivepeerToken
      BondingManager --> LivepeerToken
  ```
</ScrollableDiagram>

The LPT token contract is deployed on Ethereum mainnet and bridged to Arbitrum One. Protocol contracts read and write the bridged LPT on Arbitrum. The Confluence upgrade ([LIP-73](https://github.com/livepeer/LIPs/blob/main/LIPs/LIP-0073.md)) migrated the protocol contracts from Ethereum mainnet to Arbitrum One to reduce transaction costs and increase throughput, while keeping Ethereum's security guarantees through the rollup.

For full contract addresses and source links see <LinkArrow href="/v2/about/protocol/blockchain-contracts" label="Blockchain Contracts" newline={false} />.

<CustomDivider />

## Protocol Layer

The protocol layer is the set of off-chain coordination protocols that sit between Gateways and Orchestrators. It is what makes the network usable at production volume: every video segment, every AI inference request, every price quote runs through the protocol layer without touching the chain.

The protocol layer pairs each off-chain mechanism with a thin on-chain anchor. Discovery happens off-chain, but the addresses Gateways query were registered on-chain. Payment happens off-chain through tickets, but winning tickets settle on-chain. Verification happens off-chain through re-encoding, but slashing happens on-chain.

<DynamicTableV2
  headerList={['Mechanism', 'Off-chain component', 'On-chain anchor']}
  itemsList={[
{
  'Mechanism': 'Bonding and delegation',
  'Off-chain component': 'Client-side delegation decisions',
  'On-chain anchor': 'BondingManager.bond()',
},
{
  'Mechanism': 'Service discovery',
  'Off-chain component': 'Client queries to advertised endpoints',
  'On-chain anchor': 'ServiceRegistry.setServiceURI()',
},
{
  'Mechanism': 'Job negotiation',
  'Off-chain component': 'Peer-to-peer price quotes between Gateway and Orchestrator',
  'On-chain anchor': 'None - prices are not posted on-chain',
},
{
  'Mechanism': 'Probabilistic micropayments',
  'Off-chain component': 'Ticket generation and validation per segment',
  'On-chain anchor': 'TicketBroker.redeemWinningTicket()',
},
{
  'Mechanism': 'Verification',
  'Off-chain component': 'Segment re-encoding checks',
  'On-chain anchor': 'BondingManager.slashTranscoder()',
},
{
  'Mechanism': 'Rewards',
  'Off-chain component': 'Orchestrator-initiated reward() call each round',
  'On-chain anchor': 'Minter.reward()',
},
]}
/>

The probabilistic micropayment design is the load-bearing piece. Without it, every transcoded segment would need an on-chain transaction. With it, payments accumulate as off-chain tickets and only winning tickets settle on Arbitrum, keeping per-segment cost bounded while preserving the economic guarantees of on-chain settlement.

For the mechanisms in detail see <LinkArrow href="/v2/about/protocol/mechanisms" label="Core Mechanisms" newline={false} />.

<CustomDivider />

## Node Layer

The node layer is the software that participants run. The reference implementation is [go-livepeer](https://github.com/livepeer/go-livepeer). It is the same binary in every role - the role is selected by command-line flag.

A node started with `-broadcaster` (or `-gateway`) accepts video streams and AI requests from applications, selects Orchestrators from the on-chain registry, and dispatches jobs with attached payment tickets. A node started with `-orchestrator` registers on-chain, advertises capabilities, accepts jobs from Gateways, and coordinates GPU workers. A node started with `-transcoder` connects to an Orchestrator and performs only the video encoding work, allowing GPU capacity to scale independently of the Orchestrator controller.

<DynamicTableV2
  headerList={["Node role", "Started with", "Responsibility"]}
  itemsList={[
{
  "Node role": "Gateway",
  "Started with": "go-livepeer -gateway (or -broadcaster)",
  "Responsibility": "Receive jobs from applications, select Orchestrators, dispatch work with payment tickets, return results",
},
{
  "Node role": "Orchestrator",
  "Started with": "go-livepeer -orchestrator",
  "Responsibility": "Register on-chain, advertise capabilities, accept jobs, coordinate workers, redeem winning tickets",
},
{
  "Node role": "Transcoder",
  "Started with": "go-livepeer -transcoder -orchAddr ADDR",
  "Responsibility": "Perform video encoding work for a connected Orchestrator (O-T split configuration)",
},
{
  "Node role": "AI Runner",
  "Started with": "Docker container managed by Orchestrator process",
  "Responsibility": "Run AI inference for a specific pipeline and model on GPU",
},
]}
/>

The node layer is what interacts with both other layers. Gateways read the on-chain ServiceRegistry to discover Orchestrators, then talk to those Orchestrators directly through the protocol layer. Orchestrators read on-chain stake and bonding state, accept off-chain jobs, redeem winning tickets back on-chain, and call `reward()` once per round to claim inflation.

<CustomDivider />

## How the Layers Interact

The interaction model is best read as the lifecycle of a single piece of work. A Gateway has a video stream or an AI request. It needs an Orchestrator that can do the work, a way to pay for it, and a way to verify the result. The three layers each play one part.

The Gateway begins by reading the on-chain ServiceRegistry to find Orchestrators that have advertised the capability it needs. It then negotiates price directly with one or more candidates over the protocol layer - that negotiation never touches the chain. Once a session is open, jobs flow segment-by-segment, each carrying a probabilistic payment ticket.

Most tickets are losing tickets and never settle. A small fraction win and are redeemed by the Orchestrator on-chain through the TicketBroker, releasing accumulated ETH. Once per round, the Orchestrator also calls `reward()` to claim its share of newly minted LPT, distributed proportionally to bonded stake.

For the per-job state machine and per-workload pipeline differences see <LinkArrow href="/v2/about/network/job-pipelines" label="Job Pipelines" newline={false} />.

<CustomDivider />

## What This Architecture Buys

The layered design reflects three deliberate tradeoffs.

The blockchain handles only what must be globally agreed: stake, payments at the redemption boundary, governance, and the Active Set. Everything else is pushed off-chain, which is what makes the per-segment cost low enough to compete with centralised infrastructure.

The protocol layer is the verification surface. Gateways check that returned segments match the input - if they do not, the Gateway stops sending work. Slashing on-chain only intervenes for repeated or provable misbehaviour. Most quality enforcement happens through Gateway selection, which costs the misbehaving Orchestrator future revenue without requiring an on-chain transaction.

The node layer is interchangeable. Anyone can implement a compatible Gateway or Orchestrator against the protocol specification. go-livepeer is the reference implementation, but the protocol does not require it - what defines a compliant participant is correct interaction with the on-chain contracts and the off-chain protocol mechanisms.

<CustomDivider />

## Related Pages

<CardGroup cols={2}>
  <Card title="Protocol Design" icon="compass-drafting" href="/v2/about/protocol/design" arrow horizontal>
    Why the protocol is structured this way and the design decisions behind it.
  </Card>

  <Card title="Core Mechanisms" icon="gears" href="/v2/about/protocol/mechanisms" arrow horizontal>
    Rounds, staking, rewards, payments, slashing, and upgrades in detail.
  </Card>

  <Card title="Protocol Actors" icon="users-gear" href="/v2/about/protocol/actors" arrow horizontal>
    Orchestrators, Gateways, Delegators, and how their incentives align.
  </Card>

  <Card title="Blockchain Contracts" icon="file-contract" href="/v2/about/protocol/blockchain-contracts" arrow horizontal>
    Full contract reference with deployed addresses and source links.
  </Card>

  <Card title="Governance and Treasury" icon="building-columns" href="/v2/about/protocol/governance-and-treasury" arrow horizontal>
    LIPs, on-chain voting, and the protocol treasury.
  </Card>

  <Card title="Livepeer Token" icon="coins" href="/v2/about/protocol/livepeer-token" arrow horizontal>
    LPT token economics, inflation, and bridging.
  </Card>
</CardGroup>
