# Design Decisions This document captures the key architectural decisions made while building the at-run serverless execution system and AtmosphereConf VOD app. Decisions are attributed to who drove them. ## Core Architecture ### Serverless on AT Protocol (Mainasara) The foundational idea: store JavaScript bundles as blobs on your PDS, execute them via runners. "Your code is your data." This enables decentralized compute where anyone can run a runner and execute bundles from any PDS. ### Deno Sandbox (Claude) Use Deno subprocess for sandboxed execution instead of Bun's unsafe `import()`. Deno's permission model (`--allow-net`, `--allow-read`, etc.) maps cleanly to bundle-declared permissions, providing security without complex VM isolation. ### Manifest + Endpoint Pattern (Claude) Bundle authors declare a `manifest()` with permissions and `endpoint()` functions with handlers. The runner extracts this metadata at load time and enforces permissions at execution time. ```typescript export const bundle = manifest({ name: "my-api", permissions: { net: ["api.example.com"] }, }) export const getData = endpoint({ handler: async () => { ... }, }) ``` ### Secrets Encryption (Claude) Secrets are encrypted with the runner's public key using X25519-XSalsa20-Poly1305. Bundle authors encrypt secrets for specific runners, stored on-chain. Runners decrypt at execution time and inject as environment variables. This keeps secrets safe even though bundles are public. ### Env Permissions Derived from Secrets (Mainasara) Rather than bundle authors declaring which env vars they need, derive `--allow-env` permissions automatically from the secrets configured for that bundle on each runner. Simpler and more secure. ## Task System ### Background Jobs with Concurrency Limiting (Mainasara) Heavy operations like thumbnail generation were killing the $5 VPS. The user requested a "job paradigm" - something that queues work and returns status. ### Task Abstraction (Claude) Implemented `task()` as a variant of `endpoint()` with: - `concurrency`: max parallel executions (prevents resource exhaustion) - `cacheTtl`: result caching (avoids redundant work) - Input-based deduplication (same args = same task) Clients get `{ status: "pending" | "running" | "complete", result?: ... }` and retry until complete. ```typescript export const videoMetadata = task({ concurrency: 2, cacheTtl: 86400, // 24h handler: async ({ uri }) => { ... }, }) ``` ### Client Polling (Claude) Web client uses `callTask()` helper that retries every 2 seconds until status is "complete". Simple and works with any HTTP client. ## Video Processing ### HLS Streaming (Mainasara) Use stream.place's existing HLS infrastructure. The VOD bundle fetches playlists, rewrites relative URLs to absolute, and serves to video players. ### URL Rewriting for Local FFmpeg (Claude) HLS playlists use relative URLs. For ffmpeg to process them locally, rewrite all segment URLs to absolute (`https://vod-beta.stream.place/xrpc/...`). ### Sprite Sheet Hover Previews (Mainasara) YouTube-style thumbnail previews on hover. User wanted this specific UX. ### Sprite Implementation (Claude) Generate 8 frames at even intervals, combine into 4x2 grid using ffmpeg's `xstack` filter. Output WebVTT with `#xywh=` coordinates for each frame's position. Web component parses VTT and cycles through frames on hover. ### 2x Quality Sprites (Mainasara) Initial 160x90 frames looked blurry. User requested 320x180 for sharper previews. ## Accelerator Architecture ### Offload to Serverless GPU (Mainasara) The $5 VPS couldn't handle ffmpeg thumbnail generation at scale. User suggested Modal/RunPod with GPU acceleration. ### Runner as Gateway, Modal as Compute (Claude) Keep the runner as a lightweight API gateway handling: - Task queue (deduplication, caching) - Request routing - Secrets management Offload heavy compute to Modal. The task just calls Modal's endpoint and returns the result. ### Frontend-Side Processing with MediaBunny (Mainasara) After debugging CUDA/ffmpeg image issues on Modal, decided to handle video processing from the frontend using mediabunny.dev instead of server-side acceleration. ## Web App ### YouTube-Style Layout (Mainasara) User requested a layout matching YouTube's player page: video on left, metadata and related content on right. ### Fixed Aspect Ratio Container (Claude) Use `aspect-ratio: 16/9` on the video container to prevent layout shift when the video loads. ### Creator Profiles from Bluesky (Mainasara) Show the video creator's Bluesky profile (avatar, display name) on the video page. ### Profile Endpoint (Claude) Add `getProfile` endpoint that fetches from `public.api.bsky.app` and returns normalized profile data. ## Deployment ### Docker with Bun + Deno + FFmpeg (Claude) Single Dockerfile that installs all three runtimes. Runner uses Bun for the HTTP server, spawns Deno for sandboxed execution. ### Volume Mount for Keys (Mainasara) Runner keys stored at `~/.at-run/` on the host, mounted read-only into the container. User specified this path. ### Dokploy Deployment (Mainasara) User's platform of choice for Docker deployments. ## Social Sharing ### Share Endpoints Pattern (Mainasara) For rich link previews in a static SPA, create server endpoints that return HTML with OG meta tags then redirect to the frontend. The share button copies a link to the server endpoint, not the SPA route. ### Simplified OG Images (Claude, with Mainasara's direction) Initially attempted satori-based OG image generation with custom fonts and layouts. Encountered WASM initialization issues in the Deno sandbox. Simplified to using original thumbnails/avatars directly as OG images, with titles formatted as "Streamhut | \