An entry for the streamplace vod showcase
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

docs: add README for web frontend and runner

- apps/web: video streaming features, API integration, theming
- runner: routes, configuration, sandbox security, job scheduler

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+312 -58
+100 -58
apps/web/README.md
··· 1 - # React + TypeScript + Vite 1 + # @streamhut/web 2 2 3 - This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 3 + Video streaming frontend for AT Protocol VOD bundles. 4 4 5 - Currently, two official plugins are available: 5 + ## Overview 6 6 7 - - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs) 8 - - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) 7 + A React-based video player that streams content from AT Protocol serverless bundles via at-run. Features an editorial-style interface with theme support and profile pages. 9 8 10 - ## React Compiler 9 + ## Features 11 10 12 - The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). 11 + - **HLS Video Streaming** - Adaptive bitrate playback with quality selection 12 + - **Thumbnail Previews** - Sprite-based scrubbing with VTT timeline 13 + - **Watch History** - Local storage persistence with progress tracking 14 + - **Themes** - Light, Dark, Navy, and System (follows OS preference) 15 + - **Profile Pages** - View content by creator (`/profile/:did`) 16 + - **Batch Loading** - Efficient API calls for metadata and profiles 13 17 14 - ## Expanding the ESLint configuration 18 + ## Project Structure 15 19 16 - If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: 20 + ``` 21 + src/ 22 + ├── main.tsx # App entry, router setup 23 + ├── router.ts # Simple path-based routing 24 + ├── api.ts # API client for at-run bundle 25 + ├── lib/ 26 + │ ├── theme.ts # Theme management 27 + │ └── watchHistory.ts # Local watch progress 28 + └── ui/ 29 + ├── UI4Editorial.tsx # Main video grid + player 30 + └── ProfilePage.tsx # Creator profile view 31 + ``` 17 32 18 - ```js 19 - export default defineConfig([ 20 - globalIgnores(['dist']), 21 - { 22 - files: ['**/*.{ts,tsx}'], 23 - extends: [ 24 - // Other configs... 33 + ## Environment Variables 34 + 35 + Configure the API endpoint via environment variables: 36 + 37 + | Variable | Default | Description | 38 + |----------|---------|-------------| 39 + | `VITE_RUNNER_URL` | `https://at-run.mainasara.dev` | at-run runner URL | 40 + | `VITE_BUNDLE_PATH` | `bundle/did:plc:.../atmosphereconf-vod/latest` | Bundle path | 25 41 26 - // Remove tseslint.configs.recommended and replace with this 27 - tseslint.configs.recommendedTypeChecked, 28 - // Alternatively, use this for stricter rules 29 - tseslint.configs.strictTypeChecked, 30 - // Optionally, add this for stylistic rules 31 - tseslint.configs.stylisticTypeChecked, 42 + Create a `.env.local` for local development: 32 43 33 - // Other configs... 34 - ], 35 - languageOptions: { 36 - parserOptions: { 37 - project: ['./tsconfig.node.json', './tsconfig.app.json'], 38 - tsconfigRootDir: import.meta.dirname, 39 - }, 40 - // other options... 41 - }, 42 - }, 43 - ]) 44 + ```bash 45 + VITE_RUNNER_URL=http://localhost:3000 46 + VITE_BUNDLE_PATH=bundle/did:plc:xxx/my-vod/latest 44 47 ``` 45 48 46 - You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: 49 + ## Development 47 50 48 - ```js 49 - // eslint.config.js 50 - import reactX from 'eslint-plugin-react-x' 51 - import reactDom from 'eslint-plugin-react-dom' 51 + ```bash 52 + # Install dependencies 53 + bun install 52 54 53 - export default defineConfig([ 54 - globalIgnores(['dist']), 55 - { 56 - files: ['**/*.{ts,tsx}'], 57 - extends: [ 58 - // Other configs... 59 - // Enable lint rules for React 60 - reactX.configs['recommended-typescript'], 61 - // Enable lint rules for React DOM 62 - reactDom.configs.recommended, 63 - ], 64 - languageOptions: { 65 - parserOptions: { 66 - project: ['./tsconfig.node.json', './tsconfig.app.json'], 67 - tsconfigRootDir: import.meta.dirname, 68 - }, 69 - // other options... 70 - }, 71 - }, 72 - ]) 55 + # Start dev server 56 + bun run dev 57 + 58 + # Build for production 59 + bun run build 60 + 61 + # Preview production build 62 + bun run preview 63 + ``` 64 + 65 + ## API Integration 66 + 67 + The frontend consumes endpoints from the VOD bundle: 68 + 69 + | Endpoint | Method | Description | 70 + |----------|--------|-------------| 71 + | `listVideos` | POST | Paginated video list | 72 + | `getVideo` | POST | Single video details | 73 + | `getVideoStreams` | POST | Available quality streams | 74 + | `getPlaylist` | GET | HLS playlist (cacheable) | 75 + | `getThumbnail` | GET | Video thumbnail (cacheable) | 76 + | `getSprite` | GET | Preview sprite sheet (cacheable) | 77 + | `getVtt` | GET | VTT timeline data (cacheable) | 78 + | `getProfile` | GET | Creator profile (cacheable) | 79 + | `batchMetadataReady` | POST | Check multiple videos' metadata | 80 + | `batchProfiles` | POST | Fetch multiple profiles | 81 + 82 + ### Caching Strategy 83 + 84 + - **GET endpoints** use browser caching via `Cache-Control` headers 85 + - **Batch endpoints** reduce HTTP request count (2 requests instead of 100+) 86 + - Watch history stored in `localStorage` 87 + 88 + ## Theming 89 + 90 + Themes are managed via CSS custom properties on `[data-theme]`: 91 + 92 + ```css 93 + [data-theme="navy"] { 94 + --bg-primary: #0f172a; 95 + --text-primary: #e2e8f0; 96 + /* ... */ 97 + } 98 + ``` 99 + 100 + Available themes: 101 + - `light` - Light mode 102 + - `dark` - Dark mode 103 + - `navy` - Navy blue (default) 104 + - `system` - Follows OS preference 105 + 106 + ## Deployment 107 + 108 + Build outputs to `dist/`. Deploy as static files to any CDN or hosting service: 109 + 110 + ```bash 111 + bun run build 112 + # Deploy dist/ to your host 73 113 ``` 114 + 115 + Ensure the at-run runner is accessible and CORS-enabled for the deployed domain.
+212
packages/at-run/runner/README.md
··· 1 + # @at-run/runner 2 + 3 + HTTP server that executes AT Protocol serverless bundles in a sandboxed Deno environment. 4 + 5 + ## Overview 6 + 7 + The runner fetches JavaScript bundles from AT Protocol PDS servers and executes them in isolated Deno sandboxes. It handles permission enforcement, secret injection, job scheduling, and CORS. 8 + 9 + ## How It Works 10 + 11 + 1. **Request arrives** at `/bundle/:did/:name/:version/:endpoint` 12 + 2. **Bundle resolved** - DID document lookup finds the PDS, fetches bundle record 13 + 3. **Bundle cached** - Downloaded to `/tmp/atrun-{hash}.js` 14 + 4. **Sandbox execution** - Deno runs the bundle with enforced permissions/limits 15 + 5. **Response returned** - JSON or binary Response with CORS headers 16 + 17 + ## Requirements 18 + 19 + - [Bun](https://bun.sh) (runtime) 20 + - [Deno](https://deno.land) (sandbox execution) 21 + 22 + ```bash 23 + # Install Deno 24 + curl -fsSL https://deno.land/install.sh | sh 25 + ``` 26 + 27 + ## Routes 28 + 29 + | Route | Method | Description | 30 + |-------|--------|-------------| 31 + | `/bundle/:did/:name/:version/:endpoint` | GET/POST | Execute bundle by name | 32 + | `/at://did/collection/rkey/endpoint` | GET/POST | Execute by AT URI | 33 + | `/local/path/to/bundle.js/endpoint` | GET/POST | Local execution (dev only) | 34 + | `/health` | GET | Health check | 35 + | `/jobs/status` | GET | Scheduler status | 36 + | `/jobs/notify` | POST | Job notifications (from CLI) | 37 + | `/jobs/run` | POST | Manual job trigger | 38 + 39 + ### Request Arguments 40 + 41 + Arguments can be provided via: 42 + - **POST body** - JSON payload 43 + - **Query string** - `?args={"key":"value"}` or `?key=value` 44 + 45 + ## Configuration 46 + 47 + Create `at-run-config.json` in the working directory: 48 + 49 + ```json 50 + { 51 + "port": 3000, 52 + "did": "did:plc:your-runner-did", 53 + "devMode": false, 54 + "sandboxLocal": true, 55 + "access": { 56 + "allowedDids": ["did:plc:allowed-author"] 57 + }, 58 + "maxPermissions": { 59 + "net": true, 60 + "read": ["/tmp"], 61 + "write": ["/tmp"], 62 + "env": ["ALLOWED_VAR"] 63 + }, 64 + "maxLimits": { 65 + "timeout": 60000, 66 + "memory": 512 67 + }, 68 + "jobs": { 69 + "enabled": true, 70 + "tickIntervalMs": 10000, 71 + "dbPath": "./jobs.db" 72 + } 73 + } 74 + ``` 75 + 76 + ### Config Options 77 + 78 + | Option | Type | Description | 79 + |--------|------|-------------| 80 + | `port` | number | Server port (default: 3000) | 81 + | `did` | string | Runner's DID for secrets decryption | 82 + | `devMode` | boolean | Enable local bundle execution | 83 + | `sandboxLocal` | boolean | Sandbox local bundles in dev mode | 84 + | `access.allowedDids` | string[] | Restrict to specific bundle authors | 85 + | `maxPermissions` | object | Cap bundle permissions | 86 + | `maxLimits` | object | Cap execution limits | 87 + | `jobs.enabled` | boolean | Enable job scheduler | 88 + | `jobs.tickIntervalMs` | number | Scheduler check interval | 89 + | `jobs.dbPath` | string | SQLite database for job state | 90 + 91 + ## Sandbox Security 92 + 93 + Bundles execute in Deno with restricted permissions: 94 + 95 + - **Network** - Only allowed hosts (bundle manifest) 96 + - **File System** - Only allowed paths (typically `/tmp`) 97 + - **Environment** - Only allowed variables 98 + - **Time Limit** - Enforced timeout (default 30s) 99 + - **Memory** - Capped allocation 100 + 101 + Permissions are the **intersection** of: 102 + 1. Bundle's manifest permissions 103 + 2. Endpoint's specific permissions 104 + 3. Runner's `maxPermissions` cap 105 + 106 + ## Secret Management 107 + 108 + Runners can decrypt secrets for bundles: 109 + 110 + 1. Runner has a keypair (auto-generated, stored in `at-run-keys.json`) 111 + 2. Bundle author encrypts secrets to runner's public key via CLI 112 + 3. Runner decrypts and injects secrets as environment variables 113 + 114 + ```bash 115 + # Author encrypts secret for runner 116 + at-run secrets set my-bundle API_KEY "secret-value" --runner did:plc:runner 117 + 118 + # Secret available in bundle as Deno.env.get("API_KEY") 119 + ``` 120 + 121 + ## Job Scheduler 122 + 123 + The runner can execute scheduled jobs (cron-based): 124 + 125 + 1. Jobs registered via `at-run jobs create` 126 + 2. Runner receives notifications at `/jobs/notify` 127 + 3. Scheduler ticks and executes due jobs 128 + 4. Results logged, next run calculated 129 + 130 + Jobs persist in SQLite (`jobs.db`) and survive restarts. 131 + 132 + ## Development 133 + 134 + ```bash 135 + # Install dependencies 136 + bun install 137 + 138 + # Run in dev mode (hot reload) 139 + bun run dev 140 + 141 + # Build for production 142 + bun run build 143 + 144 + # Build standalone binary 145 + bun run build:binary 146 + ``` 147 + 148 + ### Dev Mode Features 149 + 150 + - Hot reload on code changes 151 + - `/local/` route for testing local bundles 152 + - Verbose logging with `[dev]` prefix 153 + - Bundle cache bypassed 154 + 155 + ## CORS 156 + 157 + All responses include CORS headers: 158 + 159 + ``` 160 + Access-Control-Allow-Origin: * 161 + Access-Control-Allow-Methods: GET, POST, OPTIONS 162 + Access-Control-Allow-Headers: Content-Type 163 + ``` 164 + 165 + Custom Response headers from bundles are preserved. 166 + 167 + ## Response Handling 168 + 169 + Bundles can return: 170 + 171 + - **JSON** - Automatically serialized with `Content-Type: application/json` 172 + - **Response object** - Binary data, custom headers, status codes 173 + 174 + ```typescript 175 + // JSON response 176 + return { status: "ok", data: [...] } 177 + 178 + // Binary response 179 + return new Response(imageBytes, { 180 + headers: { "Content-Type": "image/jpeg" } 181 + }) 182 + ``` 183 + 184 + Binary responses are base64-encoded through the sandbox boundary and decoded by the runner. 185 + 186 + ## Architecture 187 + 188 + ``` 189 + ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ 190 + │ Client │────▶│ Runner │────▶│ Deno │ 191 + │ (Browser) │ │ (Bun) │ │ Sandbox │ 192 + └─────────────┘ └─────────────┘ └─────────────┘ 193 + 194 + 195 + ┌─────────────┐ 196 + │ AT Proto │ 197 + │ PDS │ 198 + └─────────────┘ 199 + ``` 200 + 201 + ## Files 202 + 203 + | File | Description | 204 + |------|-------------| 205 + | `index.ts` | HTTP server, routing, execution | 206 + | `sandbox.ts` | Deno subprocess management | 207 + | `config.ts` | Configuration loading | 208 + | `secrets.ts` | Secret decryption | 209 + | `jobScheduler.ts` | Cron job scheduling | 210 + | `jobStore.ts` | SQLite job persistence | 211 + | `taskQueue.ts` | Long-running task management | 212 + | `cron.ts` | Cron expression parsing |