Commit Graph

94 Commits

Author SHA1 Message Date
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
91f23123cd Remove .env from git tracking
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 03:03:13 +00:00
gilesb
3077cd9185 Add .env to gitignore
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 02:58:56 +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
2c008812f6 Import artdag.nodes to register built-in executors
Without importing artdag.nodes, the EFFECT executor was never
registered, causing "No executor for node type: EFFECT" errors.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 00:02:48 +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
f52ec79860 Use direct HTTP API for IPFS instead of ipfshttpclient
Replace ipfshttpclient library with direct HTTP requests to IPFS API.
This fixes compatibility with newer Kubo versions (0.39.0+) which are
not supported by the outdated ipfshttpclient library.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 19:39:49 +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
552c4590c2 Expose IPFS swarm port 4001 for P2P connectivity
Enables full IPFS network participation - other nodes can
discover and fetch content from this node.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 18:51:25 +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
e25b61e6ab Add IPFS to external network for gateway access
Allows nginx to proxy to IPFS gateway at ipfs.celery-artdag.rose-ash.com

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 18:07: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
bc07a101d1 Add actor_id (user) to item_types and l2_shares tables
Multi-user support for cache items:
- item_types now includes actor_id (@user@server format)
- l2_shares now includes actor_id
- Same cache item can be owned by multiple users with different types
- Deleting severs user's connection, not the actual file
- Cache files only removed when no users reference them
- Added has_remaining_references() and cleanup_orphaned_cache_item()
- Updated all CRUD functions to include actor_id parameter

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 15:26:09 +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
0c69c9c136 Fix ipfshttpclient version (0.7.0 is latest)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 15:04:13 +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
63ec895514 Update README with config endpoints
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 03:20:04 +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
f61c82dd51 Add PYTHONPATH to Dockerfile for worker imports 2026-01-08 02:21:05 +00:00
gilesb
01f6db0621 Add debug logging to cache lookup 2026-01-08 02:11:37 +00:00
gilesb
3e12596dbf Fix cache lookup to work across processes
cache_manager.py:
- get_by_content_hash() now tries direct cache.get(content_hash)
  since uploads use content_hash as node_id
- This works even if cache index hasn't been reloaded

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 02:02:47 +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
0e4f596401 Fix render_effect to find uploads in new cache location
The legacy render_effect task was looking for inputs only at
CACHE_DIR/{hash}, but uploads now go through cache_manager which
stores them in CACHE_DIR/nodes/{node_id}/. Now uses
cache_manager.get_by_content_hash() which checks both locations.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 01:47: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