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

# pymthouse

> Identity, billing, and payment signing infrastructure for Livepeer-powered AI video applications. Self-hostable and open source.

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

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

export const TableCell = ({children, align = "left", header = false, style = {}, className = "", ...rest}) => {
  const Component = header ? "th" : "td";
  return <Component className={className} style={{
    padding: "0.75rem 1rem",
    textAlign: align,
    border: header ? "none" : "1px solid var(--lp-color-border-default)",
    ...style
  }} {...rest}>
      {children}
    </Component>;
};

export const TableRow = ({children, header = false, hover = false, style = {}, className = "", ...rest}) => {
  const rowId = `table-row-${Math.random().toString(36).substr(2, 9)}`;
  return <>
      {hover && <style>{`
          #${rowId}:hover {
            background-color: var(--lp-color-bg-card);
          }
        `}</style>}
      <tr id={rowId} className={className} style={{
    ...header && ({
      backgroundColor: "var(--lp-color-accent-strong)",
      color: "var(--lp-color-on-accent)",
      fontWeight: "bold"
    }),
    ...style
  }} {...rest}>
        {children}
      </tr>
    </>;
};

export const StyledTable = ({children, variant = "default", style = {}, className = "", ...rest}) => {
  const wrapperVariants = {
    default: {
      border: "1px solid var(--lp-color-border-default)",
      backgroundColor: "var(--lp-color-bg-card)",
      overflow: "hidden"
    },
    bordered: {
      border: "2px solid var(--lp-color-accent)",
      backgroundColor: "var(--lp-color-bg-page)",
      overflow: "hidden"
    },
    minimal: {
      border: "none",
      backgroundColor: "transparent",
      overflow: "visible"
    }
  };
  return <div data-docs-styled-table-shell className={className} style={{
    width: "100%",
    padding: 0,
    margin: 0,
    ...wrapperVariants[variant],
    ...style
  }} {...rest}>
      <table data-docs-styled-table style={{
    width: "100%",
    borderCollapse: "collapse",
    borderSpacing: 0,
    margin: 0,
    backgroundColor: "transparent"
  }}>
        {children}
      </table>
    </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>;
};

<CenteredContainer preset="readable90">
  <Tip>pymthouse is a community-built backend layer that gives your Livepeer-powered app OIDC identity, usage-based billing, and a managed payment signer - without building any of that infrastructure yourself.</Tip>
</CenteredContainer>

<CustomDivider />

Building a multi-tenant AI video application on Livepeer means solving three infrastructure problems before you write a single line of product code: authenticating your users, metering their usage, and signing probabilistic micropayment tickets on their behalf. pymthouse solves all three as a hosted or self-hosted service.

pymthouse is a community project by John ([@eliteprox](https://github.com/eliteprox)), an active Livepeer Orchestrator operator and go-livepeer contributor. The platform is open source, self-hostable, and free to use during beta.

<Warning>
  pymthouse is a community ecosystem project, not an official Livepeer Foundation product. It is in active beta development. Verify compatibility with the current go-livepeer release before deploying to production.
</Warning>

<CustomDivider />

## Core capabilities

pymthouse provides three integrated services that Livepeer application developers would otherwise build from scratch.

**Identity and authentication** - pymthouse runs a full OpenID Connect (OIDC) provider. Your application registers as an OIDC client, and pymthouse issues scoped short-lived tokens for your end users. This implements RFC 8693 token exchange, meaning it slots into existing OAuth 2.0 flows. Admin authentication uses `next-auth` with Google, GitHub, or Bearer token options.

**Multi-tenant billing** - Each developer application on pymthouse gets its own isolated OIDC client namespace, user registry, and billing plan. Plans are configurable as free, subscription, or usage-based. The Builder API lets you provision users and record usage against per-user ledgers, denominated in wei to match the on-chain payment unit and avoid floating-point precision loss.

**Remote payment signing** - pymthouse proxies signing operations to a `go-livepeer` signer sidecar. Your application calls one endpoint; pymthouse authenticates the caller, signs the Livepeer probabilistic micropayment ticket, records usage, and returns the result. The audit trail is maintained in the usage ledger.

The following diagram shows the request flow for a typical user-initiated AI inference call routed through pymthouse.

```mermaid theme={"theme":{"light":"github-light","dark":"dark-plus"}}
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#1a1a1a', 'primaryTextColor': '#fff', 'primaryBorderColor': '#2d9a67', 'lineColor': '#2d9a67', 'secondaryColor': '#0d0d0d', 'tertiaryColor': '#1a1a1a', 'background': '#0d0d0d', 'fontFamily': 'system-ui' }}}%%
sequenceDiagram
    participant App as Your Application
    participant PM as pymthouse
    participant Signer as go-livepeer signer
    participant LP as Livepeer Network

    App->>PM: POST /api/v1/oidc/token (user token exchange)
    PM-->>App: scoped access_token
    App->>PM: AI inference request + access_token
    PM->>PM: Validate token, check billing plan
    PM->>Signer: Sign micropayment ticket
    Signer-->>PM: Signed ticket
    PM->>LP: Forward request with signed payment
    LP-->>PM: Inference result
    PM->>PM: Record usage to ledger
    PM-->>App: Return inference result
```

<CustomDivider />

## System architecture

pymthouse is built on Next.js (App Router) with a PostgreSQL database managed via Drizzle ORM. The `go-livepeer` remote signer runs as a Docker sidecar or external service, communicating with pymthouse over a local HTTP API.

<StyledTable variant="bordered">
  <thead>
    <TableRow header>
      <TableCell header>Component</TableCell>
      <TableCell header>Technology</TableCell>
      <TableCell header>Responsibility</TableCell>
    </TableRow>
  </thead>

  <tbody>
    <TableRow>
      <TableCell>**Web app**</TableCell>
      <TableCell>Next.js 16 (App Router)</TableCell>
      <TableCell>Dashboard, OIDC endpoints, Builder API, Usage API</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>**Database**</TableCell>
      <TableCell>PostgreSQL via Drizzle ORM</TableCell>
      <TableCell>App registry, user ledger, usage records, signer config</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>**Signer**</TableCell>
      <TableCell>go-livepeer remote signer (Docker)</TableCell>
      <TableCell>Probabilistic micropayment ticket signing and Orchestrator communication</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>**Token security**</TableCell>
      <TableCell>PBKDF2-SHA256 + server-side pepper</TableCell>
      <TableCell>Bearer token and API key hashing at rest</TableCell>
    </TableRow>
  </tbody>
</StyledTable>

The recommended deployment splits the Next.js app to Vercel and the `go-livepeer` signer to a container host (Railway, Render, or Fly.io). A `Dockerfile.signer`, `railway.json`, and `render.yaml` are included in the repository for each target.

<CustomDivider />

## Key API endpoints

pymthouse exposes four API surfaces. All use OAuth 2.0 client credentials (RFC 6749) for machine authentication.

**OIDC endpoints** at `/api/v1/oidc` - Standard OpenID Connect discovery, JWKS, authorisation, token, and UserInfo endpoints. Use the discovery document (`/.well-known/openid-configuration`) in production integrations to avoid path drift between versions.

**Builder API** at `/api/v1/apps/{clientId}/users` - Confidential client API for provisioning end users, minting short-lived scoped tokens, and managing user attributes within a specific app namespace.

**Usage API** at `/api/v1/apps/{clientId}/usage` - Read-only, tenant-scoped endpoint for billing dashboards and per-user cost attribution. Monetary totals are returned as decimal strings of wei - parse with a BigInt-capable library.

**Signer proxy** - Accepts signed inference or transcoding requests, forwards to the `go-livepeer` signer, and writes usage records on each call.

<CustomDivider />

## Getting started

pymthouse is available as a hosted service (free during beta) and as a self-hosted open-source deployment.

**Hosted** - Create an account at [pymthouse.com](https://pymthouse.com), register your application in the dashboard, and integrate the Builder API. Time to first token is under five minutes.

**Self-hosted** - Clone the repository, configure environment variables (see `.env.example`), run database migrations, seed OIDC signing keys, and start the `go-livepeer` signer sidecar. Full deployment instructions are in `DEPLOYMENT.md` and `docs/vercel-deployment.md`.

The OIDC seed command initialises JWT signing keys required before any token issuance:

```bash icon="terminal" theme={"theme":{"light":"github-light","dark":"dark-plus"}}
npm run oidc:seed
```

After seeding, register application clients through the dashboard or the credentials endpoint:

```icon="terminal" theme={"theme":{"light":"github-light","dark":"dark-plus"}}
/api/v1/apps/{clientId}/credentials
```

Full integration documentation is at [docs.pymthouse.com](https://docs.pymthouse.com).

<CustomDivider />

## Resources

<StyledTable variant="bordered">
  <thead>
    <TableRow header>
      <TableCell header>Resource</TableCell>
      <TableCell header>Link</TableCell>
    </TableRow>
  </thead>

  <tbody>
    <TableRow>
      <TableCell>**Live platform**</TableCell>
      <TableCell>[pymthouse.com](https://pymthouse.com)</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>**Documentation**</TableCell>
      <TableCell>[docs.pymthouse.com](https://docs.pymthouse.com)</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>**GitHub repository**</TableCell>
      <TableCell>[github.com/eliteprox/pymthouse](https://github.com/eliteprox/pymthouse)</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>**DeepWiki (AI repo analysis)**</TableCell>
      <TableCell>[deepwiki.com/eliteprox/pymthouse](https://deepwiki.com/eliteprox/pymthouse)</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>**Marketplace**</TableCell>
      <TableCell>[pymthouse.com/marketplace](https://pymthouse.com/marketplace)</TableCell>
    </TableRow>
  </tbody>
</StyledTable>

<CustomDivider />

## Related pages

<CardGroup cols={2}>
  <Card title="AI authentication guide" icon="key" href="/v2/developers/guides/auth-and-security/ai-authentication" arrow horizontal>
    Set up API authentication for AI inference requests in your Livepeer application.
  </Card>

  <Card title="Production checklist" icon="list-check" href="/v2/developers/guides/production-hardening-checklist" arrow horizontal>
    Review the operational checks before moving an AI integration into production traffic.
  </Card>

  <Card title="NaaP - Network as a Platform" icon="grid-2" href="/v2/developers/build/plugins-and-extensions/overview" arrow horizontal>
    Plugin-based network portal for developers and operators building on Livepeer AI compute.
  </Card>

  <Card title="Builder opportunities" icon="hammer" href="/v2/developers/resources/grants-and-programmes" arrow horizontal>
    Explore grants, RFPs, and open-source contribution paths for ecosystem builders.
  </Card>
</CardGroup>
