Fragment and oEmbed endpoints must be accessible without authentication.
The silent auth middleware was returning 302 redirects, causing fragment
fetches from coop apps to silently fail.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- nav-item fragment handler with template
- link-card fragment handler with CID-based lookup, friendly names, batch mode
- oEmbed router at GET /oembed for media/recipe/effect/run content
- Fragment templates in app/templates/fragments/
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Check did_auth:{device_id} in Redis — if absent while user has
a session cookie, account has logged out. Clear the cookie so
next request triggers prompt=none which won't re-auth.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add /health endpoint, update healthcheck to use it
- Use configured base URL instead of internal Docker URL
- Add /health to skip prefixes for silent auth
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
FastAPI runs the last-registered middleware first on request.
device_id_middleware was inner, so silent_auth_check's early
redirect bypassed it — cookie never set.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- POST /inbox with HTTP Signature verification
- Device ID cookie tracking + adoption from account
- Silent auth checks local Redis for did_auth signals
- Replaces shared-Redis coupling with AP activity delivery
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Middleware on every GET checks if user is logged in. If not, does a
silent prompt=none redirect to account. If account has an active
session, login completes invisibly. Otherwise sets a 5-minute cooldown
cookie to avoid redirect loops.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GITHUB_REF wasn't available inside the remote SSH session.
Use Gitea's template interpolation instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- config.py: OAuth settings replace l2_server/l2_domain
- auth.py: full rewrite — login/callback/logout with itsdangerous
signed state cookies and httpx token exchange
- dependencies.py: remove l2_server assignment, fix redirect path
- home.py: simplify /login to redirect to /auth/login
- base.html: cross-app nav (Blog, Market, Account) + Rose Ash branding
- requirements.txt: add itsdangerous
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cascading regex replacements corrupted their own output: the string regex
matched CSS class names inside previously-generated span tags. Replaced with
a single-pass character tokenizer that never re-processes its own HTML output.
Also added highlighting to recipe detail page (previously had none).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Zero-downtime deploys: new container starts and passes health
check before the old one is stopped. Caddy always has a healthy
backend to proxy to.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move DATABASE_URL, ADMIN_TOKEN, ARTDAG_CLUSTER_KEY to .env
and use env_file on all services. This means docker stack deploy
no longer needs env vars sourced in the shell, and repeat deploys
won't trigger spurious restarts on unchanged services.
GPU worker gets its own .env.gpu with fully resolved cross-VPC URLs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docker stack deploy does not read .env files automatically
(unlike docker compose), so ${VAR} substitutions resolve to
empty strings. Source .env to export vars before deploying.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add gradient functions: linear, radial, and multi-stop color maps
- Add RGBA strip rotation with bilinear interpolation
- Add shadow compositing with optional Gaussian blur
- Add combined place_text_strip_fx_jax pipeline (gradient + rotation + shadow)
- Add 7 new S-expression bindings for all FX primitives
- Extract shared _composite_strip_onto_frame helper
- Fix rotation precision: snap trig values near 0/±1 to exact values,
use pixel-center convention (dim-1)/2, and parity-matched output buffers
- All 99 tests pass with zero pixel differences
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rsync was leaving dirty working trees on the deploy server, causing
git pull conflicts during manual deploys. Use git fetch + reset instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace hardcoded POSTGRES_PASSWORD, ADMIN_TOKEN, and L1 host IP
with env var references in docker-compose.yml
- Remove default password fallback from database.py and app/config.py
- Update .env.example with required POSTGRES_PASSWORD, ADMIN_TOKEN, L1_HOST
- Update README to mark DATABASE_URL as required
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add JAX text rendering with font atlas, styled text placement, and typography primitives
- Add xector (element-wise/reduction) operations library and sexp effects
- Add deferred effect chain fusion for JIT-compiled effect pipelines
- Expand drawing primitives with font management, alignment, shadow, and outline
- Add interpreter support for function-style define and require
- Add GPU persistence mode and hardware decode support to streaming
- Add new sexp effects: cell_pattern, halftone, mosaic, and derived definitions
- Add path registry for asset resolution
- Add integration, primitives, and xector tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove gpu-worker.yml (no GPU server)
- Fix ci.yml: install ssh/rsync in job container, remove GPU steps
- Remove source mounts from l1-server and l1-worker so they use image code
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add sexp_to_jax.py: JAX compiler for S-expression effects
- Use jax.random.fold_in for deterministic but varying random per frame
- Pass seed from recipe config through to JAX effects
- Fix NVENC detection to do actual encode test
- Add set_random_seed for deterministic Python random
The fold_in approach allows frame_num to be traced (not static)
while still producing different random patterns per frame,
fixing the interference pattern issue.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
MultiResolutionHLSOutput creates files in subdirectories:
- original/playlist.m3u8 instead of stream.m3u8
- original/segment_*.ts instead of segment_*.ts
The validation now checks both paths.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The problem: HLS.js caches quality playlist URLs from the master playlist.
Even when we update the master playlist CID, HLS.js keeps polling the same
static quality CID URL, so it never sees new segments.
The fix:
- Store quality-level CIDs in database (quality_playlists JSONB column)
- Generate master playlist with dynamic URLs (/runs/{id}/quality/{name}/playlist.m3u8)
- Add quality endpoint that fetches LATEST CID from database
- HLS.js now polls our dynamic endpoints which return fresh content
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- resolve_friendly_name_sync: for resolving friendly names in sync code
- get_ipfs_cid_sync: for looking up IPFS CIDs in sync code
These are needed for video streaming callbacks that can't use async/await.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- multi_res_output.py was not tracked, causing import errors
- Update gpu_output.py with recent changes
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Single player for both live rendering and completed HLS streams
- "From Start" mode plays from beginning (replay/VOD style)
- "Live Edge" mode follows rendering progress
- Uses dynamic playlist endpoint for both modes
- Removes duplicate VOD player code
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Returns progress, frame, total_frames from Celery task state
so clients can display rendering progress.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove stream_dir deletion in finally block to prevent IPFS upload failures
- Add on_progress callback to StreamInterpreter for real-time progress updates
- Task now sends progress updates to Celery state during rendering
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add "Run Again" button that reruns the recipe with same parameters
- Add "Delete" button with confirmation to delete run and artifacts
- Consolidate result display into single #action-result span
- Implement POST /runs/rerun/{recipe_id} endpoint
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add CUDA sync before encoding to ensure RGB->NV12 kernel completes
- Add debug logging for frame data validation (sum check)
- Handle GPUFrame objects in GPUHLSOutput.write()
- Fix cv2.resize for CuPy arrays (use cupyx.scipy.ndimage.zoom)
- Fix fused pipeline parameter ordering (geometric first, color second)
- Add raindrop-style ripple with random position/freq/decay/amp
- Generate final VOD playlist with #EXT-X-ENDLIST
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Return ipfs_playlist_cid from pending_runs while task is running
- Add Cache-Control: no-cache headers to prevent browser/CDN caching
- Fix streaming clients getting stale playlist CIDs
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Recipe should work on both CPU and GPU. The interpreter
auto-selects *_gpu versions when available.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The Python fallback path was reading amplitude directly from effect dict
instead of checking dynamic_params first like the CUDA kernel path does.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Zoom now driven by audio energy via core:map-range
- Ripple amplitude reads from dynamic_params in sexp_to_cuda
- Crossfade transition with zoom in/out effect
- Move git clone before COPY in Dockerfile for better caching
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When running with --pool=solo, there may already be a running event loop.
Use thread pool to run async coroutines when a loop is already running.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Downstream code expects arrays with .flags attribute, not GPUFrame.
Extract the underlying gpu/cpu array before returning.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The fallback path was passing raw numpy/cupy arrays to GPU functions
that expect GPUFrame objects with .cpu property.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>