> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blindcast.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Presign Endpoint

> Bundled presign endpoint for browser-side uploads. Enable with ENABLE_PRESIGN=true and S3 credentials.

The key server bundles a presign endpoint that generates presigned S3 PUT URLs. The [Uploader](/uploader/overview) uses this to upload encrypted segments directly from the browser to S3 — without routing bytes through your server.

## Enable presign

```bash theme={null}
docker run -d \
  -e MASTER_KEY_HEX=... \
  -e SALT_HEX=... \
  -e CORS_ORIGINS=https://app.example.com \
  -e ENABLE_PRESIGN=true \
  -e S3_BUCKET=my-video-bucket \
  -e S3_REGION=us-east-1 \
  -e AWS_ACCESS_KEY_ID=... \
  -e AWS_SECRET_ACCESS_KEY=... \
  -p 4100:4100 \
  blindcast/keyserver
```

## How it works

```mermaid theme={null}
sequenceDiagram
  participant Browser as Browser (uploader)
  participant KS as Key Server
  participant S3 as S3

  Browser->>KS: POST /presign { key: "content/vid-001/seg-0.ts", contentType: "video/mp2t" }
  KS->>KS: Validate auth + generate presigned URL
  KS-->>Browser: { url: "https://s3.../seg-0.ts?X-Amz-..." }
  Browser->>S3: PUT encrypted bytes to presigned URL
```

1. The uploader sends `POST /presign` with the S3 key and content type
2. The key server validates the JWT (if auth is enabled), then generates a presigned PUT URL
3. The uploader uploads encrypted bytes directly to S3 using the presigned URL

## Presign request format

```
POST /presign
Authorization: Bearer <token>
Content-Type: application/json

{
  "key": "content/my-video-001/seg-0.ts",
  "contentType": "video/mp2t"
}
```

## Presign response

```json theme={null}
{
  "url": "https://my-video-bucket.s3.us-east-1.amazonaws.com/content/my-video-001/seg-0.ts?X-Amz-Algorithm=..."
}
```

The presigned URL expires after 300 seconds (5 minutes) by default.

## Configuration

| Variable                | Default     | Description                              |
| ----------------------- | ----------- | ---------------------------------------- |
| `ENABLE_PRESIGN`        | `false`     | Set to `true` to enable                  |
| `S3_BUCKET`             | —           | S3 bucket name (required)                |
| `S3_REGION`             | `us-east-1` | AWS region                               |
| `AWS_ACCESS_KEY_ID`     | —           | AWS credentials                          |
| `AWS_SECRET_ACCESS_KEY` | —           | AWS credentials                          |
| `S3_ENDPOINT`           | —           | Custom S3 endpoint (for MinIO, R2, etc.) |

## S3-compatible storage

For MinIO, Cloudflare R2, or Backblaze B2, set `S3_ENDPOINT`:

```bash theme={null}
-e S3_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com
```

## Security

<Warning>
  If authentication is not configured (`AUTH_JWT_SECRET` or `AUTH_JWKS_URL`), the presign endpoint is open to anyone who can reach the key server. Always enable authentication when `ENABLE_PRESIGN=true` in production.
</Warning>

* Presign requests require the same JWT authentication as key requests (if auth is enabled)
* The key server validates that the S3 key starts with an allowed prefix (default: `content/`)
* Presigned URLs are short-lived (5 minutes) and single-use
