- 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>
- 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>
- 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>
- 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>
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>
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>
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>
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>
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>
- 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>
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>
- 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>
- 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>
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>
- 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>
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>
- 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>
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>
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>
- 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>
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>
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>
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>
- 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>
- 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>
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>
- 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>
- 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>
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>
- 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>
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>
- 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>
- 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>
- 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>
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>
- 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>
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>
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>
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>