# How to Configure the Speech-to-Speech Server for Production Deployment with Authentication

> Secure your Hugging Face Speech-to-Speech server for production. Learn how to configure authentication and usage limits with OAuth and environment variables for a robust deployment.

- Repository: [Hugging Face/speech-to-speech](https://github.com/huggingface/speech-to-speech)
- Tags: how-to-guide
- Published: 2026-08-10

---

**The Hugging Face Speech-to-Speech demo server enables production authentication and usage limits by deploying as a Hugging Face Space with OAuth enabled, setting `LOAD_BALANCER_URL` and `SPACE_ID` environment variables, and letting [`demo/auth.py`](https://github.com/huggingface/speech-to-speech/blob/main/demo/auth.py) and [`demo/server.py`](https://github.com/huggingface/speech-to-speech/blob/main/demo/server.py) automatically wire Hugging Face OAuth and tier-based metering.**

Running the [Hugging Face Speech-to-Speech](https://github.com/huggingface/speech-to-speech) demo in production requires more than starting the FastAPI server locally. When deployed as a Hugging Face Space, the repository provides built-in authentication, per-user rate limiting, and organization-based tier resolution. This guide explains how to configure the speech-to-speech server for production deployment with authentication using the actual source code implementation.

## How Production Authentication Works

The authentication system activates only when both `LOAD_BALANCER_URL` and `SPACE_ID` environment variables are present. This safety mechanism ensures OAuth and metering never interfere with local development.

### Core Components

| Component | File | Responsibility |
|-----------|------|--------------|
| **OAuth handler** | [`demo/auth.py`](https://github.com/huggingface/speech-to-speech/blob/main/demo/auth.py) | Implements Hugging Face OAuth, resolves user tiers (`pro`, `org`, `free`), mints anonymous cookies |
| **Server entry point** | [`demo/server.py`](https://github.com/huggingface/speech-to-speech/blob/main/demo/server.py) | Calls `auth.attach(app)` during startup, exposes `/api/me` endpoint |
| **Usage limiter** | [`demo/limiter.py`](https://github.com/huggingface/speech-to-speech/blob/main/demo/limiter.py) | Stores per-user daily counters, enforces budgets, signs/verifies anonymous cookies |

In [`demo/server.py`](https://github.com/huggingface/speech-to-speech/blob/main/demo/server.py), the following logic gates all production features:

```python

# server.py (excerpt)

LIMITER_ENABLED = bool(LOAD_BALANCER_URL) and bool(SPACE_ID)

# Wire HF OAuth before the app serves (no-op unless the OAuth env is present).

# Sign-in only matters when we're metering (prod Space), so gate it on that.

AUTH_ENABLED = LIMITER_ENABLED and auth.attach(app)

```

When `AUTH_ENABLED` evaluates to `True`, the server exposes `/api/me` and protects the WebRTC signaling endpoints with tier-aware usage budgets.

## Required Environment Variables

Configure these variables in your Space's Settings → Environment variables:

| Variable | Purpose | Required |
|----------|---------|----------|
| **`LOAD_BALANCER_URL`** | URL of the Speech-to-Speech load balancer; triggers metered, sign-in-aware mode | **Yes** for production |
| **`SPACE_ID`** | Identifier of the Space (`owner/space`); auto-injected by Hugging Face platform | **Yes** (auto-provided) |
| **`OAUTH_CLIENT_ID`** | Enables Hugging Face OAuth flow; auto-provided when `hf_oauth: true` is in README | Auto-provided |
| **`SERPER_API_KEY`** | API key for the Google search proxy (`/api/search`) | Optional |
| **`RTC_ICE_SERVERS`** | JSON list of ICE servers for WebRTC behind strict firewalls | Optional |
| **`UNLIMITED_ORGS`** | Comma/space-separated org usernames with unlimited usage | Optional |

These are read in [`demo/server.py`](https://github.com/huggingface/speech-to-speech/blob/main/demo/server.py) (lines 60–82) and [`demo/auth.py`](https://github.com/huggingface/speech-to-speech/blob/main/demo/auth.py) (lines 38–46, 66–70).

## Step-by-Step Production Deployment

### 1. Build the Container

The repository includes a root `Dockerfile` that installs the package and copies the `demo/` directory:

```dockerfile
FROM python:3.11-slim

COPY . /app
WORKDIR /app
RUN pip install --no-cache-dir .[all]

EXPOSE 7860
CMD ["uvicorn", "demo.server:app", "--host", "0.0.0.0", "--port", "7860"]

```

Build and test locally:

```bash
docker build -t s2s-demo .
docker run -p 7860:7860 s2s-demo

```

### 2. Create and Configure the Hugging Face Space

Add `hf_oauth: true` to your Space's [`README.md`](https://github.com/huggingface/speech-to-speech/blob/main/README.md):

```markdown

# Speech-to-Speech Demo

hf_oauth: true

```

Push your code to the Space. The platform automatically injects `OAUTH_CLIENT_ID` and `SPACE_ID`.

### 3. Set Production Environment Variables

In the Space's Settings → Environment variables, add:

| Name | Example Value |
|------|---------------|
| `LOAD_BALANCER_URL` | `https://lb.my-company.com` |
| `SERPER_API_KEY` | `sk_...` (if using search) |
| `RTC_ICE_SERVERS` | `[{"urls":"turn:turn.example.com:3478","username":"user","credential":"pass"}]` |
| `UNLIMITED_ORGS` | `my-org enterprise-team` |

The server starts with authentication enabled when `LOAD_BALANCER_URL` and `SPACE_ID` are both present.

### 4. Bypass Authentication for Direct S2S Access (Optional)

Set `SPEECH_TO_SPEECH_URL` to a direct backend URL. When non-empty, this disables **all** load-balancer, limiter, and authentication logic:

```bash
export SPEECH_TO_SPEECH_URL="wss://direct-backend.example.com"

```

Use this for internal deployments where you handle authentication upstream.

## How Authentication Is Enforced at Runtime

### The `/api/me` Endpoint

Once `AUTH_ENABLED` is true, the server exposes:

```python

# server.py excerpt

@app.get("/api/me")
async def me(request: Request) -> dict:
    if not AUTH_ENABLED:
        return {"enabled": False}
    # Returns: {enabled: true, loggedIn: bool, tier: str, remaining: int, reason: str?}

    return auth.user_view(request)

```

Call this from your client to display login status and remaining budget:

```javascript
fetch("/api/me")
  .then(r => r.json())
  .then(info => {
    if (!info.enabled) {
      console.log("Local mode – no authentication");
      return;
    }
    console.log("Logged in:", info.loggedIn);
    console.log("Tier:", info.tier);        // "pro", "org", "free", "anon"
    console.log("Remaining:", info.remaining);
  });

```

### Identity Resolution and Tier Assignment

When a protected request arrives, [`demo/server.py`](https://github.com/huggingface/speech-to-speech/blob/main/demo/server.py) calls `auth.resolve_identity`:

```python

# auth.py excerpt

async def resolve_identity(request: Request) -> tuple[str, list[str], str | None]:
    # Returns: (tier, keys, set_cookie_header)

```

- **Signed-in users**: Tier derived from `auth.resolve_tier` via org membership or PRO status
- **Anonymous users**: `ANON_COOKIE` minted and signed with `limiter.sign_cookie`

The `keys` returned are hashed via `limiter.hash_key` and used to debit daily budgets. Exceeded budgets yield HTTP 429.

## Optional: Grant Unlimited Access to Organizations

Add organization slugs to `UNLIMITED_ORGS` for internal teams:

```bash
export UNLIMITED_ORGS="huggingface my-research-lab"

```

Members of these organizations bypass all usage limits while still requiring authentication.

## Key Files Reference

| File | Lines | Purpose |
|------|-------|---------|
| [`demo/auth.py`](https://github.com/huggingface/speech-to-speech/blob/main/demo/auth.py) | 38–46, 66–90 | OAuth flow, tier resolution, cookie handling |
| [`demo/server.py`](https://github.com/huggingface/speech-to-speech/blob/main/demo/server.py) | 60–82, 100–120 | Environment parsing, `auth.attach()`, `/api/me` |
| [`demo/limiter.py`](https://github.com/huggingface/speech-to-speech/blob/main/demo/limiter.py) | 20–50 | Budget enforcement, cookie signing |
| `Dockerfile` | 1–12 | Production container definition |

## Summary

- **Deploy as a Hugging Face Space** with `hf_oauth: true` in README.md to enable OAuth
- **Set `LOAD_BALANCER_URL`** to trigger production authentication and metering
- **`SPACE_ID` and `OAUTH_CLIENT_ID`** are auto-injected by the platform
- **`SPEECH_TO_SPEECH_URL`** bypasses all auth for direct backend access
- **Optional variables** (`SERPER_API_KEY`, `RTC_ICE_SERVERS`, `UNLIMITED_ORGS`) extend functionality

The [`demo/auth.py`](https://github.com/huggingface/speech-to-speech/blob/main/demo/auth.py) and [`demo/server.py`](https://github.com/huggingface/speech-to-speech/blob/main/demo/server.py) implementation ensures authentication only activates when both infrastructure variables are present, protecting local development from accidental lockout.

## Frequently Asked Questions

### What happens if I don't set `LOAD_BALANCER_URL`?

The server runs in **local development mode**. `LIMITER_ENABLED` evaluates to `False`, `AUTH_ENABLED` becomes `False`, and all endpoints remain unauthenticated with no usage limits. This is the safe default for testing.

### Can I use authentication without Hugging Face Spaces?

No. The OAuth implementation in [`demo/auth.py`](https://github.com/huggingface/speech-to-speech/blob/main/demo/auth.py) specifically targets Hugging Face's OAuth provider. The `auth.attach(app)` function expects `OAUTH_CLIENT_ID` in the format provided by Spaces. For non-Space deployments, implement custom authentication or use `SPEECH_TO_SPEECH_URL` with an upstream proxy.

### How do anonymous cookies prevent abuse?

Anonymous users receive a salted, signed cookie (`ANON_COOKIE`) via `limiter.sign_cookie`. The signature verification in [`demo/limiter.py`](https://github.com/huggingface/speech-to-speech/blob/main/demo/limiter.py) prevents cookie tampering, and the hash-based key derivation ensures each anonymous session has isolated usage tracking without requiring login.

### What tiers exist and how are they assigned?

The `auth.resolve_tier` function assigns:
- **`pro`** — User has an active Hugging Face PRO subscription
- **`org`** — User belongs to an organization with either enterprise features or listing in `UNLIMITED_ORGS`
- **`free`** — Authenticated user without PRO or special org status
- **`anon`** — Unauthenticated user with cookie-based tracking