---
name: wzrrd-publish
description: Publish static Sites and signed-in Pro videos to wzrrd.sh. Use for sharing agent-built pages, demos, receipts, docs, diagrams, static artifacts, or cloud-processed video watch pages.
---

# wzrrd publish

Use `wzrrd` for agent-first static Site and Pro video publishing. Command results are JSON on
stdout with `next_actions`; progress belongs on stderr.

Public CLI and skill distribution repo: `wzrrd-sh/wzrrd-cli`.
Contract: `https://wzrrd.sh/openapi.json`.
API catalog: `https://wzrrd.sh/.well-known/api-catalog`.

## Install

```bash
curl -fsSL https://wzrrd.sh/install.sh | bash
```

If `~/.local/bin` is not on `PATH`, add it or call `~/.local/bin/wzrrd` directly.

## Static Sites: publish first, authenticate only when needed

```bash
wzrrd publish --file ./site --slug demo
```

- `--file` accepts one HTML file or a directory with root `index.html`.
- Anonymous publishing needs no login. It is the fastest agent-safe path.
- Anonymous Sites expire after 24 hours and are `noindex` by default.
- Return both the live `url` and `claimUrl`. Opening `claimUrl` lets a human sign in and make
  the Site permanent.
- Add `--index` only when the user explicitly asks for search indexing.
- Current anonymous limits are 5 MB total, 1 MB per file, 50 files, and 10 publishes per
  IP per hour.

Tracked projects can set `site.slug` and `publish.source` in `wzrrd.config.json`; then
`wzrrd publish` can run without guessing flags. Schema: `https://wzrrd.sh/schema/config.json`.

Authenticated Sites can be permanent or explicitly expiring:

```bash
wzrrd publish --file ./site --slug demo --expires-in 7d
wzrrd site status --slug demo
wzrrd site set --slug demo --title "Demo" --description "Tiny preview" --indexing index
wzrrd site set --slug demo --expires-in 7d
wzrrd site set --slug demo --permanent
```

## Authentication without agent spin loops

Static anonymous publishing does not need login. Pro video and owned Site mutations do.

```bash
wzrrd auth-status
```

`wzrrd login` is a Better Auth device flow that requires one human browser approval **and** an
interactive CLI process that remains alive to exchange the approved device code for a saved
session. If `auth-status` has no live session, ask the human to run `wzrrd login` in their own
terminal and wait for completion. Then rerun `auth-status`. An unattended run exits with
`status: "awaiting_human_approval"`; `--no-open` does not complete or save a resumable session.
Never loop on device approval.

## Pro video: upload and follow the cloud Workflow

```bash
wzrrd video upload ./demo.mp4 --title "My demo"
```

The CLI creates an authenticated upload session, streams the file directly to an insert-only
presigned R2 PUT, completes verification, and follows one durable cloud Workflow through
Stream encoding, Deepgram transcription, and subtitle generation for `en`, `es`, `pt`, `fr`,
`de`, and `ja`.

- The final JSON receipt contains `watchUrl`, `shareSlug`, and `next_actions`.
- `--no-wait` returns after upload completion starts the Workflow.
- The current single-PUT ceiling is 5 GiB. Larger files are rejected with `sourceUrl` guidance.
- Videos are `unlisted` by default; the unguessable watch URL is the share link.
- A 403 saying video publishing is a Pro feature is a real entitlement refusal. Surface it to
  the user; do not retry or bypass it.

Follow-up commands:

```bash
wzrrd video status <slug>
wzrrd video trace <slug>
wzrrd video transcribe <slug>
wzrrd video transcribe --force <slug>
wzrrd video revoke <slug>
```

- `status` returns Cloudflare Workflow runtime state plus bounded D1 phase history.
- `trace` reconstructs the correlated Analytics Engine request timeline.
- `transcribe --force` starts a fresh Workflow generation after terminal work.
- `revoke` tombstones the share, deletes the provider copy and R2 original, verifies R2
  absence, and leaves failed cleanup retryable.

Verify a completed video like an agent:

```bash
curl -fsS https://wzrrd.sh/v/<slug> >/dev/null
curl -fsS https://wzrrd.sh/v/<slug>/captions.vtt >/dev/null
wzrrd video trace <slug>
```

Translated captions use `/v/<slug>/captions.es.vtt`, `.pt.vtt`, `.fr.vtt`, `.de.vtt`, and
`.ja.vtt` after translation finishes. Revoke test uploads when they are no longer needed.

## Raw API upload sequence

Prefer the CLI because it preserves token isolation and emits useful receipts. Direct API
clients must follow the contract exactly:

1. `POST /api/videos/uploads` with the wzrrd bearer session.
2. `PUT` the bytes to the returned top-level `uploadUrl` using only `upload.headers`. Never
   forward the wzrrd bearer token or cookies to R2.
3. `POST /api/videos/uploads/{shareSlug}/complete` with the wzrrd bearer session.
4. `GET /api/videos/{shareSlug}/status` until the Workflow reaches a terminal state.
5. `POST /api/videos/{shareSlug}/revoke` when cleanup is required.

See `https://wzrrd.sh/openapi.json` for request and response schemas. Provider callbacks and
operator-only testing routes are not normal client tools.

## MCP

A stateless Streamable HTTP MCP server is available at `https://wzrrd.sh/mcp`. POST carries
JSON-RPC; GET returns 405 because there is no SSE listener; OPTIONS handles CORS preflight.
Bearer auth is mandatory. Protected-resource metadata is at
`https://wzrrd.sh/.well-known/oauth-protected-resource`; pass the same wzrrd bearer credential
used by the HTTP API. Its OpenAPI-derived tools are `listSites`,
`publishSite`, `createVideoUpload`, `getVideoStatus`, `transcribeVideo`, and `revokeVideo`.
Pro enforcement is unchanged. Local files cannot cross the remote MCP boundary, so use the CLI
for local video upload or provide a public `sourceUrl` through MCP.

## Organization Profiles

```bash
wzrrd org profile status
wzrrd org profile enable --handle my-handle
wzrrd site profile --slug demo --listed true
```

Profiles are explicit and opt-in. Public routes are `https://wzrrd.sh/@my-handle` and
`https://wzrrd.sh/@my-handle/feed.json`.

## Diagnose

```bash
wzrrd doctor && wzrrd auth-status
```

Check the root app, wildcard router, API/auth state, and saved session before guessing.
