···11-# React + TypeScript + Vite
11+# @streamhut/web
2233-This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
33+Video streaming frontend for AT Protocol VOD bundles.
4455-Currently, two official plugins are available:
55+## Overview
6677-- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
88-- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
77+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.
981010-## React Compiler
99+## Features
11101212-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).
1111+- **HLS Video Streaming** - Adaptive bitrate playback with quality selection
1212+- **Thumbnail Previews** - Sprite-based scrubbing with VTT timeline
1313+- **Watch History** - Local storage persistence with progress tracking
1414+- **Themes** - Light, Dark, Navy, and System (follows OS preference)
1515+- **Profile Pages** - View content by creator (`/profile/:did`)
1616+- **Batch Loading** - Efficient API calls for metadata and profiles
13171414-## Expanding the ESLint configuration
1818+## Project Structure
15191616-If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
2020+```
2121+src/
2222+├── main.tsx # App entry, router setup
2323+├── router.ts # Simple path-based routing
2424+├── api.ts # API client for at-run bundle
2525+├── lib/
2626+│ ├── theme.ts # Theme management
2727+│ └── watchHistory.ts # Local watch progress
2828+└── ui/
2929+ ├── UI4Editorial.tsx # Main video grid + player
3030+ └── ProfilePage.tsx # Creator profile view
3131+```
17321818-```js
1919-export default defineConfig([
2020- globalIgnores(['dist']),
2121- {
2222- files: ['**/*.{ts,tsx}'],
2323- extends: [
2424- // Other configs...
3333+## Environment Variables
3434+3535+Configure the API endpoint via environment variables:
3636+3737+| Variable | Default | Description |
3838+|----------|---------|-------------|
3939+| `VITE_RUNNER_URL` | `https://at-run.mainasara.dev` | at-run runner URL |
4040+| `VITE_BUNDLE_PATH` | `bundle/did:plc:.../atmosphereconf-vod/latest` | Bundle path |
25412626- // Remove tseslint.configs.recommended and replace with this
2727- tseslint.configs.recommendedTypeChecked,
2828- // Alternatively, use this for stricter rules
2929- tseslint.configs.strictTypeChecked,
3030- // Optionally, add this for stylistic rules
3131- tseslint.configs.stylisticTypeChecked,
4242+Create a `.env.local` for local development:
32433333- // Other configs...
3434- ],
3535- languageOptions: {
3636- parserOptions: {
3737- project: ['./tsconfig.node.json', './tsconfig.app.json'],
3838- tsconfigRootDir: import.meta.dirname,
3939- },
4040- // other options...
4141- },
4242- },
4343-])
4444+```bash
4545+VITE_RUNNER_URL=http://localhost:3000
4646+VITE_BUNDLE_PATH=bundle/did:plc:xxx/my-vod/latest
4447```
45484646-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:
4949+## Development
47504848-```js
4949-// eslint.config.js
5050-import reactX from 'eslint-plugin-react-x'
5151-import reactDom from 'eslint-plugin-react-dom'
5151+```bash
5252+# Install dependencies
5353+bun install
52545353-export default defineConfig([
5454- globalIgnores(['dist']),
5555- {
5656- files: ['**/*.{ts,tsx}'],
5757- extends: [
5858- // Other configs...
5959- // Enable lint rules for React
6060- reactX.configs['recommended-typescript'],
6161- // Enable lint rules for React DOM
6262- reactDom.configs.recommended,
6363- ],
6464- languageOptions: {
6565- parserOptions: {
6666- project: ['./tsconfig.node.json', './tsconfig.app.json'],
6767- tsconfigRootDir: import.meta.dirname,
6868- },
6969- // other options...
7070- },
7171- },
7272-])
5555+# Start dev server
5656+bun run dev
5757+5858+# Build for production
5959+bun run build
6060+6161+# Preview production build
6262+bun run preview
6363+```
6464+6565+## API Integration
6666+6767+The frontend consumes endpoints from the VOD bundle:
6868+6969+| Endpoint | Method | Description |
7070+|----------|--------|-------------|
7171+| `listVideos` | POST | Paginated video list |
7272+| `getVideo` | POST | Single video details |
7373+| `getVideoStreams` | POST | Available quality streams |
7474+| `getPlaylist` | GET | HLS playlist (cacheable) |
7575+| `getThumbnail` | GET | Video thumbnail (cacheable) |
7676+| `getSprite` | GET | Preview sprite sheet (cacheable) |
7777+| `getVtt` | GET | VTT timeline data (cacheable) |
7878+| `getProfile` | GET | Creator profile (cacheable) |
7979+| `batchMetadataReady` | POST | Check multiple videos' metadata |
8080+| `batchProfiles` | POST | Fetch multiple profiles |
8181+8282+### Caching Strategy
8383+8484+- **GET endpoints** use browser caching via `Cache-Control` headers
8585+- **Batch endpoints** reduce HTTP request count (2 requests instead of 100+)
8686+- Watch history stored in `localStorage`
8787+8888+## Theming
8989+9090+Themes are managed via CSS custom properties on `[data-theme]`:
9191+9292+```css
9393+[data-theme="navy"] {
9494+ --bg-primary: #0f172a;
9595+ --text-primary: #e2e8f0;
9696+ /* ... */
9797+}
9898+```
9999+100100+Available themes:
101101+- `light` - Light mode
102102+- `dark` - Dark mode
103103+- `navy` - Navy blue (default)
104104+- `system` - Follows OS preference
105105+106106+## Deployment
107107+108108+Build outputs to `dist/`. Deploy as static files to any CDN or hosting service:
109109+110110+```bash
111111+bun run build
112112+# Deploy dist/ to your host
73113```
114114+115115+Ensure the at-run runner is accessible and CORS-enabled for the deployed domain.
+212
packages/at-run/runner/README.md
···11+# @at-run/runner
22+33+HTTP server that executes AT Protocol serverless bundles in a sandboxed Deno environment.
44+55+## Overview
66+77+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.
88+99+## How It Works
1010+1111+1. **Request arrives** at `/bundle/:did/:name/:version/:endpoint`
1212+2. **Bundle resolved** - DID document lookup finds the PDS, fetches bundle record
1313+3. **Bundle cached** - Downloaded to `/tmp/atrun-{hash}.js`
1414+4. **Sandbox execution** - Deno runs the bundle with enforced permissions/limits
1515+5. **Response returned** - JSON or binary Response with CORS headers
1616+1717+## Requirements
1818+1919+- [Bun](https://bun.sh) (runtime)
2020+- [Deno](https://deno.land) (sandbox execution)
2121+2222+```bash
2323+# Install Deno
2424+curl -fsSL https://deno.land/install.sh | sh
2525+```
2626+2727+## Routes
2828+2929+| Route | Method | Description |
3030+|-------|--------|-------------|
3131+| `/bundle/:did/:name/:version/:endpoint` | GET/POST | Execute bundle by name |
3232+| `/at://did/collection/rkey/endpoint` | GET/POST | Execute by AT URI |
3333+| `/local/path/to/bundle.js/endpoint` | GET/POST | Local execution (dev only) |
3434+| `/health` | GET | Health check |
3535+| `/jobs/status` | GET | Scheduler status |
3636+| `/jobs/notify` | POST | Job notifications (from CLI) |
3737+| `/jobs/run` | POST | Manual job trigger |
3838+3939+### Request Arguments
4040+4141+Arguments can be provided via:
4242+- **POST body** - JSON payload
4343+- **Query string** - `?args={"key":"value"}` or `?key=value`
4444+4545+## Configuration
4646+4747+Create `at-run-config.json` in the working directory:
4848+4949+```json
5050+{
5151+ "port": 3000,
5252+ "did": "did:plc:your-runner-did",
5353+ "devMode": false,
5454+ "sandboxLocal": true,
5555+ "access": {
5656+ "allowedDids": ["did:plc:allowed-author"]
5757+ },
5858+ "maxPermissions": {
5959+ "net": true,
6060+ "read": ["/tmp"],
6161+ "write": ["/tmp"],
6262+ "env": ["ALLOWED_VAR"]
6363+ },
6464+ "maxLimits": {
6565+ "timeout": 60000,
6666+ "memory": 512
6767+ },
6868+ "jobs": {
6969+ "enabled": true,
7070+ "tickIntervalMs": 10000,
7171+ "dbPath": "./jobs.db"
7272+ }
7373+}
7474+```
7575+7676+### Config Options
7777+7878+| Option | Type | Description |
7979+|--------|------|-------------|
8080+| `port` | number | Server port (default: 3000) |
8181+| `did` | string | Runner's DID for secrets decryption |
8282+| `devMode` | boolean | Enable local bundle execution |
8383+| `sandboxLocal` | boolean | Sandbox local bundles in dev mode |
8484+| `access.allowedDids` | string[] | Restrict to specific bundle authors |
8585+| `maxPermissions` | object | Cap bundle permissions |
8686+| `maxLimits` | object | Cap execution limits |
8787+| `jobs.enabled` | boolean | Enable job scheduler |
8888+| `jobs.tickIntervalMs` | number | Scheduler check interval |
8989+| `jobs.dbPath` | string | SQLite database for job state |
9090+9191+## Sandbox Security
9292+9393+Bundles execute in Deno with restricted permissions:
9494+9595+- **Network** - Only allowed hosts (bundle manifest)
9696+- **File System** - Only allowed paths (typically `/tmp`)
9797+- **Environment** - Only allowed variables
9898+- **Time Limit** - Enforced timeout (default 30s)
9999+- **Memory** - Capped allocation
100100+101101+Permissions are the **intersection** of:
102102+1. Bundle's manifest permissions
103103+2. Endpoint's specific permissions
104104+3. Runner's `maxPermissions` cap
105105+106106+## Secret Management
107107+108108+Runners can decrypt secrets for bundles:
109109+110110+1. Runner has a keypair (auto-generated, stored in `at-run-keys.json`)
111111+2. Bundle author encrypts secrets to runner's public key via CLI
112112+3. Runner decrypts and injects secrets as environment variables
113113+114114+```bash
115115+# Author encrypts secret for runner
116116+at-run secrets set my-bundle API_KEY "secret-value" --runner did:plc:runner
117117+118118+# Secret available in bundle as Deno.env.get("API_KEY")
119119+```
120120+121121+## Job Scheduler
122122+123123+The runner can execute scheduled jobs (cron-based):
124124+125125+1. Jobs registered via `at-run jobs create`
126126+2. Runner receives notifications at `/jobs/notify`
127127+3. Scheduler ticks and executes due jobs
128128+4. Results logged, next run calculated
129129+130130+Jobs persist in SQLite (`jobs.db`) and survive restarts.
131131+132132+## Development
133133+134134+```bash
135135+# Install dependencies
136136+bun install
137137+138138+# Run in dev mode (hot reload)
139139+bun run dev
140140+141141+# Build for production
142142+bun run build
143143+144144+# Build standalone binary
145145+bun run build:binary
146146+```
147147+148148+### Dev Mode Features
149149+150150+- Hot reload on code changes
151151+- `/local/` route for testing local bundles
152152+- Verbose logging with `[dev]` prefix
153153+- Bundle cache bypassed
154154+155155+## CORS
156156+157157+All responses include CORS headers:
158158+159159+```
160160+Access-Control-Allow-Origin: *
161161+Access-Control-Allow-Methods: GET, POST, OPTIONS
162162+Access-Control-Allow-Headers: Content-Type
163163+```
164164+165165+Custom Response headers from bundles are preserved.
166166+167167+## Response Handling
168168+169169+Bundles can return:
170170+171171+- **JSON** - Automatically serialized with `Content-Type: application/json`
172172+- **Response object** - Binary data, custom headers, status codes
173173+174174+```typescript
175175+// JSON response
176176+return { status: "ok", data: [...] }
177177+178178+// Binary response
179179+return new Response(imageBytes, {
180180+ headers: { "Content-Type": "image/jpeg" }
181181+})
182182+```
183183+184184+Binary responses are base64-encoded through the sandbox boundary and decoded by the runner.
185185+186186+## Architecture
187187+188188+```
189189+┌─────────────┐ ┌─────────────┐ ┌─────────────┐
190190+│ Client │────▶│ Runner │────▶│ Deno │
191191+│ (Browser) │ │ (Bun) │ │ Sandbox │
192192+└─────────────┘ └─────────────┘ └─────────────┘
193193+ │
194194+ ▼
195195+ ┌─────────────┐
196196+ │ AT Proto │
197197+ │ PDS │
198198+ └─────────────┘
199199+```
200200+201201+## Files
202202+203203+| File | Description |
204204+|------|-------------|
205205+| `index.ts` | HTTP server, routing, execution |
206206+| `sandbox.ts` | Deno subprocess management |
207207+| `config.ts` | Configuration loading |
208208+| `secrets.ts` | Secret decryption |
209209+| `jobScheduler.ts` | Cron job scheduling |
210210+| `jobStore.ts` | SQLite job persistence |
211211+| `taskQueue.ts` | Long-running task management |
212212+| `cron.ts` | Cron expression parsing |