Previously only displayed the first input. Now shows all inputs with
their names from the recipe (variable_inputs and fixed_inputs).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add activity_id column to l2_shares table
- Store activity_id when publishing to L2
- Link to /activities/{activity_id} instead of /assets/{name}
- Falls back to asset link if no activity_id
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
After publishing, link to /activities/{activity_id} instead of
/assets/{asset_name} so user sees the published run.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The tasks/ directory for 3-phase execution was shadowing the old tasks.py.
Renamed to legacy_tasks.py and updated all imports.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
New files:
- claiming.py - Redis Lua scripts for atomic task claiming
- tasks/analyze.py - Analysis Celery task
- tasks/execute.py - Step execution with IPFS-backed cache
- tasks/orchestrate.py - Plan orchestration (run_plan, run_recipe)
New API endpoints (/api/v2/):
- POST /api/v2/plan - Generate execution plan
- POST /api/v2/execute - Execute a plan
- POST /api/v2/run-recipe - Full 3-phase pipeline
- GET /api/v2/run/{run_id} - Get run status
Features:
- Hash-based task claiming prevents duplicate work
- Parallel execution within dependency levels
- IPFS-backed cache for durability
- Integration with artdag planning module
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Copy storage_providers.py from L2 (Pinata, web3.storage, NFT.Storage,
Infura, Filebase, Storj, local storage providers)
- Add storage management endpoints: GET/POST/PATCH/DELETE /storage
- Add provider-specific pages at /storage/type/{provider_type}
- Include connection testing via POST /storage/{id}/test
- Add HTML UI pages with dark theme matching L2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Database: Add description field, remove unique constraint to allow
multiple configs of same provider type
- UI: Main page shows provider types as cards with counts
- UI: Per-type page (/storage/type/{type}) for managing configs
- API: Add get_user_storage_by_type() for filtered queries
- Form: Add description field for distinguishing configs
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Properly unpack cache_manager.put() tuple to get IPFS CID and store
it in PostgreSQL via database.create_cache_item(). This fixes the
"Output has no IPFS CID - cannot publish" error when publishing
from L1 to L2.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Prepares L1 for distributed storage integration with user-provided
storage backends (Pinata, web3.storage, local). The storage config
is synced from L2 or can be configured locally per actor.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Track user tokens in Redis set (artdag:user_tokens:{username})
- Register token when user authenticates via /auth
- Add /auth/revoke-user endpoint to revoke all user tokens
- L2 calls this on logout to revoke scoped tokens
Fixes logout not working across L1/L2 (scoped tokens differ from L2 token).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add revoke_token() and is_token_revoked() functions using Redis
- Check revocation in get_verified_user_context()
- Add /auth/revoke endpoint for L2 to call on logout
- Revoked tokens stored with 30-day expiry (matching token lifetime)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
iOS Safari blocks shared cookies. Now L2 can redirect to
L1/auth?auth_token=xxx after login, and L1 sets its own
first-party cookie.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
L1 can't redirect users to login because it doesn't know which L2
server they use. Users must log in directly at their L2 server,
and the shared cookie will authenticate them on L1.
- Replace "Log in" links with "Not logged in" text
- Remove /login and /register routes
- Keep /logout to just clear cookie and redirect home
- Remove unused DEFAULT_L2_SERVER config
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- video_src_for_request now returns /raw endpoint for video source
- /raw endpoint sets proper Content-Type based on file format
- /raw endpoint sets filename with extension for downloads
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The EFFECT node executor uses register_effect() to look up effects.
Added dog effect registration so DAG recipes can use effect: dog.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
After content negotiation fix, /cache/{hash} returns HTML for
browsers. Embedded <img> tags need /raw to get actual image data.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove output_name from publish form and endpoint
- Assets on L2 are now named by their content_hash
- All inputs, recipe, and output referenced by content_hash
- Simplified publish flow - no user input needed
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The /cache/{hash} endpoint now defaults to HTML for browsers. Raw
data was being returned for requests without explicit text/html in
Accept header (e.g., link clicks). JSON is only returned when
explicitly requested. Raw data is served only from /cache/{hash}/raw.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When cache metadata has type "recipe", return that instead of
auto-detecting (which returns "unknown" for YAML files). This
ensures L2 can properly register recipes as inputs.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Recipe detail page now shows the full YAML source
- Shows "View on L2" link if recipe is shared to L2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The get_cache_manager() singleton wasn't initializing with Redis,
so workers couldn't see files uploaded via the API server.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Include artdag-client.tar.gz package
- Add /download/client route to serve the package
- Add "Download Client" link in nav bar
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
- 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>