ekx-cloudflare-streamlisted
Install: claude install-skill Ekinoxis-evm/claude-skills
# Cloudflare Stream
Video delivery for a course platform. Courses are **paid**, so every video sits behind a
signed URL; nothing is public.
## Setup
```bash
npm i jose
```
```
CF_STREAM_ACCOUNT_ID= # Cloudflare account id
CF_STREAM_API_TOKEN= # API token with Stream:Edit
CF_STREAM_KEY_ID= # signing key id
CF_STREAM_PEM= # signing private key, BASE64-encoded
CF_STREAM_CUSTOMER_CODE= # the customer-<code> subdomain
```
All server-only. `CF_STREAM_PEM` is base64 because a PEM's newlines do not survive most
env-var UIs intact.
## Signing a playback token
```ts
import { SignJWT } from "jose";
import { createPrivateKey } from "node:crypto";
export async function signStreamToken(videoUid: string, opts: { clientIp?: string } = {}) {
const pem = Buffer.from(process.env.CF_STREAM_PEM!, "base64").toString("utf-8");
const privateKey = createPrivateKey(pem);
const payload: Record<string, unknown> = { kid: process.env.CF_STREAM_KEY_ID };
if (opts.clientIp && opts.clientIp !== "unknown") {
payload.accessRules = [
{ type: "ip.src", ip: [`${opts.clientIp}/32`], action: "allow" },
{ type: "any", action: "block" },
];
}
return new SignJWT(payload)
.setProtectedHeader({ alg: "RS256", kid: process.env.CF_STREAM_KEY_ID })
.setSubject(videoUid)
.setExpirationTime("1h")
.setNotBefore(Math.floor(Date.now() / 1000) - 5)
.sign(privateKey);
}
```
### Three non-obvious require