Commit Graph

75 Commits

Author SHA1 Message Date
gilesb
ebb692ee94 Fix run_recipe endpoint to use content-addressable run_id
The run_recipe endpoint was still using uuid.uuid4() instead of
compute_run_id(). Now it:
- Computes deterministic run_id from inputs + recipe
- Checks L1 cache before running
- Checks L2 and pulls from IPFS if needed
- Only runs Celery if output not found

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 11:12:52 +00:00
gilesb
e4b9657d1b Add content-addressable runs - runs identified by hash of inputs + recipe
- Add run_cache table for fast run_id -> output lookup
- compute_run_id() computes deterministic run_id from inputs + recipe
- create_run checks L1 cache then L2 before running Celery
- If output exists on L2 but not L1, pulls from IPFS
- Saves run results to cache on completion

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 11:05:18 +00:00
gilesb
5344fe263f Remove /ui redirect routes, add 404 page, update README
- Remove obsolete /ui, /ui/login, /ui/register, /ui/logout redirects
- Fix /ui/login links to use /login directly
- Add styled 404 page for HTML requests
- Add Web UI section to README documenting routes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 10:42:14 +00:00
gilesb
1194251788 Fix broken View on L2 links - remove incorrect /ui prefix
The L2 server route is /asset/{name}, not /ui/asset/{name}.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 10:25:17 +00:00
gilesb
9c158ff884 Fix media list duplicates and cache browse link
- Database: Use DISTINCT ON to deduplicate items by content_hash
- Database: Count unique content_hashes in count_user_items
- Server: Fix media card link from /ui/cache to /cache
- Server: Use /raw endpoint for image thumbnails
- Server: Add seen_hashes dedup in media list iteration

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 10:17:35 +00:00
gilesb
ad63774acd Add L2 links to run detail page and publish success message
- Run detail page now shows "Published to L2" with link when already published
- Publish success message now includes "View on L2" link

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 09:48:25 +00:00
gilesb
a0a4c08b9a Use Redis for cache indexes - enables multi-worker scaling
The cache_manager now uses Redis hashes for the content_index and
ipfs_cids mappings. This allows multiple uvicorn workers to share
state, so files added by one worker are immediately visible to all
others.

- Added redis_client parameter to L1CacheManager
- Index lookups check Redis first, then fall back to in-memory
- Index updates go to both Redis and JSON file (backup)
- Migrates existing JSON indexes to Redis on first load
- Re-enabled workers=4 in uvicorn

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 04:19:00 +00:00
gilesb
6fc3562d22 Remove workers from L1 - causes cache inconsistency
With multiple workers, each process has its own in-memory cache index.
Files added by one worker aren't visible to others, causing intermittent
404 errors.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 04:02:51 +00:00
gilesb
83244aaa05 Filter out recipes from media list
Recipes have their own section under /recipes, so exclude them
from the media list by checking node_type == "recipe".

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 03:59:03 +00:00
gilesb
5ba66ed11e Record L2 share when publishing runs from UI
When ui_publish_run publishes a run to L2, now properly records the
share in the local database so the UI can display the L2 badge and
published status.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 03:51:36 +00:00
gilesb
e88f9812a6 Fix content negotiation for browser navigation
Check Sec-Fetch-Mode: navigate header for direct browser access.
This ensures /cache/{hash} shows HTML detail page when navigating
directly, but still serves raw files for embedded <img>/<video> tags.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 03:48:35 +00:00
gilesb
92c64f1037 Fix uvicorn workers - use import string
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 03:41:30 +00:00
gilesb
c40c681d2e Add uvicorn workers and Redis timeouts
- Run uvicorn with 4 workers to handle concurrent requests
- Add socket_timeout and socket_connect_timeout to Redis client (5s)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 03:34:13 +00:00
gilesb
121496eb53 Fix get_user_context_from_cookie to be async
Make get_user_context_from_cookie and get_user_from_cookie async
since they call async get_verified_user_context. Update all callers.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 03:21:47 +00:00
gilesb
d3468cd47c Add L2 sync button and improve publish status display
- Add "Sync with L2" button on media page to fetch user's outbox
- Link asset names to L2 asset pages in publish status
- Add green "L2" badge to media list for published items
- Create /user/sync-l2 and /ui/sync-l2 endpoints

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 03:18:48 +00:00
gilesb
2933dc4e05 Fix blocking sync calls in async endpoints
- Wrap verify_token_with_l2 HTTP calls with asyncio.to_thread()
- Wrap Redis load_run/save_run calls with asyncio.to_thread()
- Add _check_celery_task_sync helper to batch Celery backend calls
- Fix list_all_runs/list_all_recipes blocking in listing endpoints
- Fix cache_manager sync calls in discard endpoints
- Fix find_runs_using_content blocking calls

This prevents event loop blocking during L2 auth verification,
Redis operations, and Celery task status checks.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 02:52:51 +00:00
gilesb
45826138ca Store provenance on IPFS instead of local files
- Add add_json() to ipfs_client for storing JSON data
- Update render_effect task to store provenance on IPFS
- Update execute_dag task to store DAG provenance on IPFS
- Add provenance_cid field to RunStatus model
- Extract provenance_cid from task results

Provenance is now immutable and content-addressed, enabling:
- Cross-L2 verification
- Bitcoin timestamping for dispute resolution
- Complete audit trail on IPFS

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 02:40:38 +00:00
gilesb
51358a2d5f Add timing logs to runs and cache endpoints
Log timestamps for each operation in get_run and get_cached to
diagnose slowness:
- load_run from Redis
- Celery AsyncResult and task.ready()
- cache_file operations
- database queries for cache metadata

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 02:24:06 +00:00
gilesb
0e4feeb999 Add content negotiation to /cache/{content_hash}
- Browsers get HTML detail page with video/image preview
- API clients with Accept: application/json get metadata JSON
- Other requests get raw file
- Add /cache/{content_hash}/raw for explicit file downloads
- Remove old /cache/{content_hash}/detail endpoint
- Update all /detail links to use clean /cache/{hash} URL

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 02:17:46 +00:00
gilesb
486cdb5d7d Fix build_dag_from_recipe to use correct Node API
- Use keyword arguments for Node constructor
- Pass inputs list to Node instead of calling non-existent add_edge
- Two-pass approach: create SOURCE nodes first, then resolve input
  names to content-addressed IDs for dependent nodes
- Properly set output node using resolved ID

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 23:56:53 +00:00
gilesb
22472e250f Include ipfs_cid in republish payload to L2
When republishing/updating an asset on L2, now sends the IPFS CID
so L2 can update its record and pin the content.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 23:21:02 +00:00
gilesb
c73e79fe28 Save IPFS CID to database when caching files
- Make cache_file() async and save ipfs_cid to cache_items table
- Update all call sites to use await
- Add create_cache_item call in upload endpoint

Fixes IPFS info not showing for uploaded files.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 19:02:52 +00:00
gilesb
66be6b3c9a Fix f-string escaping in home page template
Escape {run_id} and {hash} as {{run_id}} and {{hash}} to
prevent NameError in f-string interpolation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 18:32:00 +00:00
gilesb
9877e61b5d Send ipfs_cid when publishing cache to L2
L1 now includes IPFS CID in publish requests so L2 can
pin content on its own IPFS node for federation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 18:20:01 +00:00
gilesb
c0c3096e48 Add configurable IPFS gateway URL with local gateway as primary
- IPFS_GATEWAY_URL env var for local gateway (e.g., https://ipfs.celery-artdag.rose-ash.com)
- Local gateway shown first with green button when configured
- Removed Pinata from public gateways list

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 18:09:51 +00:00
gilesb
808e754e7f Add IPFS CID display with public gateway links
- Cache detail page shows IPFS section with CID and links to
  ipfs.io, dweb.link, Cloudflare, and Pinata gateways
- Media list cards show purple "IPFS" badge for items with CID
- JSON API response includes ipfs_cid field

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 17:56:29 +00:00
gilesb
591c0aad4c Show logged-in user on home page instead of login button
Convert static HOME_HTML to render_home_html() function that
dynamically shows user info with link to L2 profile when
authenticated, or login button when not logged in.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 17:51:09 +00:00
gilesb
c917a0539d Migrate server.py from JSON metadata to PostgreSQL database
- Replace all load_cache_meta/save_cache_meta calls with database functions
- Update get_user_cache_hashes to async, query both DB and legacy JSON
- Replace get_user_from_cookie with get_user_context_from_cookie throughout
- Update all endpoints to use UserContext (ctx) instead of plain username
- Update render_page calls to use ctx.actor_id
- Add high-level database helper functions:
  - save_item_metadata, load_item_metadata, update_item_metadata
  - save_l2_share, get_user_items, count_user_items
- Keep legacy JSON functions for backwards compatibility during migration

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 16:45:22 +00:00
gilesb
66beda70c4 Move auth to L2 only, display @user@server format
- L1 never handles credentials - redirects to L2 for login/register
- L2 sets shared cookie (domain=.rose-ash.com) for cross-subdomain auth
- Display logged-in user as @user@server (ActivityPub format)
- Remove login/register form handling from L1
- Add L1_PUBLIC_URL env var for redirect callbacks
- Rename /ui/cache-list to /ui/media-list
- Update nav links to use clean URLs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 15:32:55 +00:00
gilesb
c0a98dd0ff Rename Cache to Media in navigation, add login to home page
- Change navigation tab from "Cache" to "Media"
- Change /cache list route to /media
- Add Recipes link to home page navigation
- Add Login button to home page
- Update all active_tab="cache" to active_tab="media"
- Update "Back to cache" links to "Back to media"

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 15:18:40 +00:00
gilesb
5e6353d150 Fix /recipes redirect loop by merging duplicate routes
Removed duplicate /recipes route that was causing infinite redirect.
Now single route handles both HTML and JSON responses directly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 15:10:45 +00:00
gilesb
ba244b9ebc Add PostgreSQL + IPFS backend, rename configs to recipes
- Add PostgreSQL database for cache metadata storage with schema for
  cache_items, item_types, pin_reasons, and l2_shares tables
- Add IPFS integration as durable backing store (local cache as hot storage)
- Add postgres and ipfs services to docker-compose.yml
- Update cache_manager to upload to IPFS and track CIDs
- Rename all config references to recipe throughout server.py
- Update API endpoints: /configs/* -> /recipes/*
- Update models: ConfigStatus -> RecipeStatus, ConfigRunRequest -> RecipeRunRequest
- Update UI tabs and pages to show Recipes instead of Configs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 14:58:29 +00:00
gilesb
4639a98231 lists of shares. job deletion only deltes outputs 2026-01-08 03:38:14 +00:00
gilesb
4a99866602 Add config/recipe support for DAG-based jobs
- Add PyYAML dependency for parsing config files
- Add Pydantic models: VariableInput, FixedInput, ConfigStatus, ConfigRunRequest
- Add Redis storage functions for configs
- Add config YAML parsing with variable and fixed input detection
- Add config API endpoints: upload, list, get, delete, run
- Add config UI: Configs tab, list page, detail page with run form
- Add HTMX endpoints for config operations
- Add pinning on publish: configs and their fixed inputs are pinned
  when runs from configs are published to L2
- Clean up debug logging in cache_manager

Config YAML format supports:
- Fixed inputs: resolve asset hashes from registry
- Variable inputs: marked with `input: true`, filled at run time
- DAG definition with nodes and edges
- Registry of assets and effects

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 03:17:50 +00:00
gilesb
f23a721816 Use local pinned metadata for deletion checks instead of L2 API
- Add is_pinned(), pin(), _load_meta(), _save_meta() to L1CacheManager
- Update can_delete() and can_discard_activity() to check local pinned status
- Update run deletion endpoints (API and UI) to check pinned metadata
- Remove L2 shared check fallback from run deletion
- Fix L2SharedChecker to return True on error (safer - prevents accidental deletion)
- Update tests for new pinned behavior

When items are published to L2, the publish flow marks them as pinned
locally. This ensures items remain non-deletable even if L2 is unreachable,
and both outputs AND inputs of published runs are protected.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 02:44:18 +00:00
gilesb
01f6db0621 Add debug logging to cache lookup 2026-01-08 02:11:37 +00:00
gilesb
13b93ab17c Add delete button for runs in UI
- Add delete_html section to run detail page with delete button
- Add /ui/runs/{run_id}/discard HTMX endpoint
- Failed runs can always be deleted without restrictions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 01:53:26 +00:00
gilesb
6142c8bbab Allow failed runs to always be deleted
Failed runs have no output to protect, so they can be deleted
without checking L2 shared status or activity records.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 01:48:23 +00:00
gilesb
ff195a7ce5 Add multi-step DAG execution support
tasks.py:
- Import artdag DAG, Node, Engine, Executor
- Register executors for effect:dog, effect:identity, SOURCE
- Add execute_dag task for running full DAG workflows
- Add build_effect_dag helper for simple effect-to-DAG conversion

server.py:
- Add use_dag and dag_json fields to RunRequest
- Update create_run to support DAG mode
- Handle both legacy render_effect and new execute_dag result formats
- Import new tasks (execute_dag, build_effect_dag)

The DAG engine executes nodes in topological order with automatic
caching. This enables multi-step pipelines like: source -> effect1 ->
effect2 -> output.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 01:45:29 +00:00
gilesb
034c7542c4 Fix cache listing to include files from new structure
- Update list_all() to scan cache_dir for legacy files directly
  (old files stored as CACHE_DIR/{hash}, not CACHE_DIR/legacy/)
- Update cache listing endpoints to use cache_manager.list_all()
  instead of iterating CACHE_DIR.iterdir() directly
- This ensures uploaded files appear in the cache UI regardless
  of whether they're in the old or new cache structure

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 01:37:17 +00:00
gilesb
a97c6309d5 Fix deletion rules: runs deletable, cache items protected
- Run deletion: Handle legacy runs without activity records by
  checking L2 shared status directly (instead of failing)
- Cache deletion: Check Redis runs in addition to activity store
  to prevent deleting inputs/outputs that belong to runs
- Add find_runs_using_content() helper to check if content_hash
  is used as input or output of any run

This fixes the inverted deletion logic where runs couldn't be
deleted but their cache items could.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 01:29:03 +00:00
gilesb
f8ec42b445 Refactor cache access to use cache_manager consistently
- Remove symlink hack from cache_file() - no longer needed
- Add get_cache_path() helper for content_hash lookups
- Update all CACHE_DIR / content_hash patterns to use cache_manager
- Fix cache_manager.get_by_content_hash() to check path.exists()
- Fix legacy path lookup (cache_dir not legacy_dir)
- Update upload endpoint to use cache_manager.put()

This ensures cache lookups work correctly for both legacy files
(stored directly in CACHE_DIR) and new files (stored in nodes/).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 01:21:11 +00:00
gilesb
26768b5942 Fix cache file serving after cache_manager integration
- Update get_cached endpoint to check cache_manager first, then legacy
- Update get_cached_mp4 endpoint similarly
- Make cache_file() create symlink in legacy location for backward compat

Fixes issue where newly cached outputs weren't being served because
they were stored in new location but served from legacy location.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 01:08:40 +00:00
gilesb
e4fd5eb010 Integrate artdag cache with deletion rules
- Add cache_manager.py with L1CacheManager wrapping artdag Cache
- Add L2SharedChecker for checking published status via L2 API
- Update server.py to use cache_manager for storage
- Update DELETE /cache/{content_hash} to enforce deletion rules
- Add DELETE /runs/{run_id} endpoint for discarding runs
- Record activities when runs complete for deletion tracking
- Add comprehensive tests for cache manager

Deletion rules enforced:
- Cannot delete items published to L2
- Cannot delete inputs/outputs of runs
- Can delete orphaned items
- Runs can only be discarded if no items are shared

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 00:51:18 +00:00
gilesb
9a399cfcee Add clean URLs with content negotiation and pagination for L1
- Add content negotiation (HTML for browsers, JSON for APIs)
- Clean URLs: /runs, /cache, /run/{id}, /cache/{hash}/detail
- Auth routes: /login, /logout, /register with clean URLs
- Add render_page() helper for consistent page layout
- Add infinite scroll pagination for HTML views
- Add API pagination with ?page=1&limit=20
- Redirect old /ui/* routes to clean URLs
- Update nav links to use clean URLs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 22:55:44 +00:00
gilesb
b2e4f81edf Add cache pinning and discard functionality
- Pin items when published (published, input_to_published)
- Discard endpoint (DELETE /cache/{hash}) refuses pinned items
- UI shows pin status and discard button on cache detail
- Run publish also pins output and all inputs for provenance

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 21:36:52 +00:00
gilesb
a3a37c568a Add metadata editing UI to cache detail page
- Added /ui/cache/{hash}/meta-form endpoint for HTMX form
- Origin selector (self vs external URL)
- Description and tags fields
- Publish to L2 with asset name
- Update on L2 for already-published items

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 21:22:48 +00:00
gilesb
5f58bf117e Increase L2 publish timeout to 30 seconds
L2 needs to call back to L1 to fetch run details, which can take
longer than 10 seconds especially with network latency.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 21:13:44 +00:00
gilesb
3436a22a94 Fix iOS video playback in list views
Use video_src_for_request() for iOS MP4 transcoding in:
- ui_runs partial
- ui_cache_list partial
- ui_run_partial (also added request parameter)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 20:58:40 +00:00
gilesb
1e95badddb Update UI to use Tailwind CSS with dark theme
- Replace custom CSS with Tailwind CSS via CDN
- Consistent dark theme across all pages
- Responsive layouts with mobile-friendly breakpoints
- Updated: home, login/register, runs list, cache list, run detail, cache detail
- Modern card-based UI with hover states and status badges

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 20:44:11 +00:00